Source code analysis of Spring IOC container prestart process

In the application, it is by creating commonly ClassPathXmlApplicationContext or AnnotationConfigApplicationContext the two bottom subclasses to start the Spring IOC container:

  • ClassPathXmlApplicationContext: xmlFile configuration edition
  • AnnotationConfigApplicationContext: annotation

Since it is increasingly popular to create our beans based on Java annotations, this article focuses on the annotated version.

AnnotationConfigApplicationContextClass relation structure of

Let’s start by looking at where we started

public class Main {

	public static void main(String[] args) {
		new AnnotationConfigApplicationContext(Config.class);
	}
	@Configuration
	public static class Config{}}Copy the code

The demo is simple. So, what’s going on here? Maybe we can see AnnotationConfigApplicationContext class relationship structure:

We can see the top AnnotationConfigApplicationContext has two top interface:

  • BeanFactory: Spring’s core interface, a pure bean container, mainly defines andBeanThe correlation method of
  • ResourceLoader: resource loader, definedgetResourcemethods

Inherits from three parent classes:

  • DefaultResourceLoader: the DefaultResourceLoader that implements three ways of loading resources

    1. Load resources using path

    2. Load resources through the classpath

    3. Load resources through urls

  • AbstractApplicationContext: implements the ApplicationContext interface of the abstract class, major functions

    1. Implements the core method for starting the IOC container: refresh()

    2. Publish event

    3. A number of getBean-related operations are implemented based on subclasses, primarily through the abstract getBeanFactory method

    4. A large number of empty methods left for subclass extension

    5. Message internationalization

  • GenericApplicationContext:

    1. Using the combination way of introducing the bottom of the BeanFactory implementation class: DefaultListableBeanFactory

    2. Defines the registerBean related operations, is achieved by DefaultListableBeanFactory

As you can see, ApplicationContext is literally an ApplicationContext, and bean operations and container management are still implemented by our BeanFactory.

Ready to start

1. Create our instance:AnnotationConfigApplicationContext

new AnnotationConfigApplicationContext(Config.class);
Copy the code

2. To enterAnnotationConfigApplicationContextA constructor

public AnnotationConfigApplicationContext(Class
       ... annotatedClasses) {
    this(a); register(annotatedClasses); refresh(); }Copy the code

3. Call our empty constructor, which instantiates our parent class first

3.1 instantiationDefaultResourceLoader

public DefaultResourceLoader(a) {
	this.classLoader = ClassUtils.getDefaultClassLoader();
}
Copy the code

ClassUtils. GetDefaultClassLoader () there are two major step operation

// The class loader that gets the thread context
ClassLoader cl = = Thread.currentThread().getContextClassLoader();
if(cl == null) // If this parameter is null, the application class loader is the system class loader
    cl = ClassLoader.getSystemClassLoader();
Copy the code

Here we are not in a Tomcat environment, so we return AppClassLoader

3.2 instantiationAbstractApplicationContext

// Assign an initial value to BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> BeanFactoryPostProcessor = new ArrayList<>();
public AbstractApplicationContext(a) {
    // Introduce a resource resolver
    this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver(a) {
    return new PathMatchingResourcePatternResolver(this);
}
Copy the code

3.3 instantiationGenericApplicationContext

public GenericApplicationContext(a) {
    // Introduce the BeanFactory implementation
    this.beanFactory = new DefaultListableBeanFactory();
}
Copy the code

3.4 Instantiate yourself

public AnnotationConfigApplicationContext(a) {
    // Initialize the annotation-based bean definition scanner
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // Initialize the bean definition scanner based on the CLASspath
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Copy the code
3.4.1 trackAnnotatedBeanDefinitionReaderInitialization process
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    / / is our AnnotationConfigApplicationContext registry
    this.registry = registry;
    // Introduces a Conditional expression calculator to handle @conditional annotations
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    // Register all annotation-related postprocessors
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
Copy the code

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
	registerAnnotationConfigProcessors(registry, null);
}
Copy the code

RegisterAnnotationConfigProcessors (registry, null), which mainly do the following things:

  • The two references for DefaultListableBeanFactory assignment

    // A dependent collator for beans with Priority, Order annotations, and Ordered interface added
    beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
    // @autowire candidate parser
    beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    Copy the code
  • Bean definitions with six post-processors registered to the container

    Registers the post-processor for the configuration class

    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME);
    Copy the code

    Register a post-processor that handles @Autowired annotations

    RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME);
    Copy the code

    Register a post-processor that handles @required annotations (deprecated as of version 5.1)

    RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME);
    Copy the code

    Register backend handlers that handle jSR-250 specification annotations, @Resource, @postConstruct, @Predestroy

    RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME);
    Copy the code

    Register a post-processor that handles the @EventListener annotation

    RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    def.setSource(source);
    registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME);
    Copy the code

    Factory registered event listeners, give the EventListenerMethodProcessors used above

    RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
    def.setSource(source);
    registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME);
    Copy the code
3.4.2 ClassPathBeanDefinitionScannerInitialization process

It goes through a series of constructor passes

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
    this(registry, true);
}

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
    this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                      Environment environment) {
    this(registry, useDefaultFilters, environment,
         (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}
Copy the code

The constructor method for the final implementation

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                      Environment environment, @Nullable ResourceLoader resourceLoader) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
	// Defaults to true
    if (useDefaultFilters) {
        // Register the default filter
        registerDefaultFilters();
    }
    // Set the environment
    setEnvironment(environment);
    // Set the resource loader
    setResourceLoader(resourceLoader);
}
Copy the code

RegisterDefaultFilters method

protected void registerDefaultFilters(a) {
    // Add a filter to scan @Component annotations so that @Controller, @service...
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // Annotations to the JSR-250 specification
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
    }
    catch (ClassNotFoundException ex) {
    }
    try {
        // Comments on the JSR-330 specification
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
    }
    catch (ClassNotFoundException ex) {
    }
}
Copy the code

4. The constructor is executedregister(annotatedClasses)Method to register the bean definition of the configuration class in the container

public void register(Class
       ... annotatedClasses) {
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
    / / is used here just AnnotatedBeanDefinitionReader sweep of initialization code
    //annotatedClasses are the custom configuration class config.class passed in at the entry
    this.reader.register(annotatedClasses);
}
Copy the code
public void register(Class
       ... annotatedClasses) {
    for(Class<? > annotatedClass : annotatedClasses) {// Here we pass only one loopregisterBean(annotatedClass); }}Copy the code
public void registerBean(Class
        annotatedClass) {
    // Spring implements the do start method
    doRegisterBean(annotatedClass, null.null.null);
}
Copy the code
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
                        @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    // Encapsulate the class in the bean definition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    Return false because the configuration class does not use the @Conditional annotation
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }

    abd.setInstanceSupplier(instanceSupplier);
    // Parse the scope defined by the bean
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name ! =null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    // Handle ordinary bean definition annotations, @lazy @primary @dependson @role @description
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    if(qualifiers ! =null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            }
            else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            }
            else {
                abd.addQualifier(newAutowireCandidateQualifier(qualifier)); }}}for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
        customizer.customize(abd);
    }

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    // Determine whether proxy encapsulation is required according to the proxy-mode property in scopeMetadata. The default value is no
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // Register the bean definition in the container
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
} 
Copy the code

With most of the preparatory work done, you are ready to call the Refresh method to start the IOC container.

I plan to spend 30 days systematically organizing my knowledge of Spring source code:

Spring Source code series
  1. Spring source code analysis of IOC container prestart process (completed)
  2. BeanFactory Architecture for Spring source analysis (completed)
  3. Spring Source Analysis BeanFactoryPostProcessor call process (completed)
  4. Spring source analysis of the Bean creation process
  5. What are cyclic dependencies and solutions for Spring source analysis
  6. AOP for Spring source analysis from parsing to invocation
  7. Spring source analysis of transaction management (1), transaction management is a feature of Spring as a container, summarize his basic implementation and principle
  8. Spring source analysis of transaction management (below), about his underlying thing isolation and thing propagation principle, focus on the analysis
Spring Mvc source code series
  1. The SpringMvc architecture
  2. SpringMvc source code analysis Handler parsing process
  3. Request chain process for SpringMvc source code analysis
Mybatis source series

tentative


Chase more, can pay attention to my public number: geek time, share purely for fun, but also have a sense of achievement, the author of this article to this first