Spring source code series (two) : SpringBoot automatic assembly principle analysis

Preface:

SpringBoot’s autowiring is often asked in interviews. This article will start with general answers or answers that can be found on the Internet.

If you want to deal with the interview or are looking for answers, see part 1 of SpringBoot Auto assembly interview answers, if you want to understand the principle of auto assembly, understand the context, don’t forget to see part 2 of SpringBoot auto assembly principle analysis.

Interview answers for SpringBoot autoassembly

First let’s find the main startup class, as shown below.

Then click on the @SpringBootApplication annotation and there are many annotations. Here we will focus on the @enableAutoConfiguration annotation, which translates to enable automatic configuration.

Then we continue to point in, find a @ Import annotations, inside a AutoConfigurationImportSelector class. Let’s keep clicking.

And find a way to two very important inside getAutoConfigurationEntry and getCandidateConfigurations. GetAutoConfigurationEntry method name write very clear, for automatic assembly of the entrance. Then a getCandidateConfigurations method, in the first line of the inside is SpringBoot complete automatic assembly operations. Let’s analyze this method.

  1. LoadFactoryNames () passes two arguments, We first open the first getSpringFactoriesLoaderFactoryClass (), the result is returned EnableAutoConfiguration. The class, the second method getBeanClassLoader () returns a load of the bean Class loader.

    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    				getBeanClassLoader());
    Copy the code
  2. Then we clicked on loadFactoryNames () method, found that we get from the first method EnableAutoConfiguration. Class in the form of parameters passed to the factoryType, Then we by getName () method to obtain the fully qualified name of a class name: org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration

    public static List<String> loadFactoryNames(Class<? > factoryType,@Nullable ClassLoader classLoader) {
    		String factoryTypeName = factoryType.getName();
    		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    	}
    Copy the code
  3. Then go ahead and click loadSpringFactories(), first fetching the result from the cache if it already has one, or the online one from the URL if it doesn’t.

    1. This is what we’ll find imported at the top when we write XML filesdtdSince we are not connected to the Internet, our Spring project can run well because we cache these files locally and we don’t need to be connected to the Internet to get them from the local cache when we start.

    The getResources() method gets the configuration file path with the FACTORIES_RESOURCE_LOCATION parameter, whose value is meta-INF/spring.Factories

  4. We opened the spring.factories configuration file in the spring-boot-autoconfigure package. Remember before we get the fully qualified name of org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration?

    We load the classes using the fully qualified name, and then put the class names into a List of configurations.

  5. Finally, back to our initial getAutoConfigurationEntry method, getCandidateConfigurations () method has been performed, And we’ve put the spring.Factories configuration file into the Configurations collection, and finally we’re going to new an AutoConfigurationEntry object, Was finished in our initial @ Import AutoConfigurationImportSelector classes, completed the automatic assembly

    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    Copy the code

Analysis of SpringBoot automatic assembly principle

Both Spring and SpringBoot, at startup will initialize the container, one of the most important way is to AbstractApplicationContext the refresh method.

When we perform to refresh methods of invokeBeanFactoryPostProcessors (the beanFactory), Will perform the first BeanDefinitionRegistry postProcessBeanDefinitionRegistry method, in which there is a very important class ConfigurationClassPostProcessor. This class is a post-processor class that contributes to the construction of the BeanFactory, which is loaded into the Spring container when the obtainFreshBeanFactory method in Refresh resolves the < Component-Scan /> tag. (For details on how to parse < component-scan /> see my other article juejin.cn/post/708130…)

The main function of ConfigurationClassPostProcessor is as follows:

  • Parse the Configuration class with @Configuration
  • Parse the packages that @ComponentScan scans
  • Parse the packages that @ComponentScans scan
  • Parse the @import annotation

We perform postProcessBeanDefinitionRegistry method, there will be a parse. Parse (candidates) method, This method is used to resolve beanDefinitions with @Controller, @import, @ImportResource, @ComponentScan, @ComponentScans, and @Bean. We want to realize automatic assembly must be automatic Import some classes, so naturally need to use @ Import annotations, to introduce AutoConfigurationImportSelector this class. Let’s begin the detailed parsing of @import.

  1. We start with the getImports(sourceClass) method, which has two sets: the imports collection is used to store classes decorated with the @import annotation, and the Visited collection is used to implement recursive calls. And the main execution method collectImports()

  2. If we go to the collectImports() method, we see that the collectImports() method is also executed, so we see that we are using recursion.

    Why use recursion?

    SpringBoot starts with a primary boot class. When we first enter getImports() and collectImports(), the sourceClass is our primary boot class. There’s an @SpringBootApplication annotation on the main bootstrap class, and the construction of that, as we talked about above, is an @enableAutoConfiguration annotation, Inside a @ Import (AutoConfigurationImportSelector. Class), we Import the class can finish automatic assembly.

    But you have layer upon layer of @import annotations on the main startup class, so we need recursion. Find all the classes decorated with the @import annotation and add them to the imports collection

    / / sourceClass getAnnotations () to get to the main start-up annotation on the class
    for (SourceClass annotation : sourceClass.getAnnotations()) {
    				// annotation.getMetadata().getClassName() gets the metadata of the annotation, and then gets the class name
    				String annName = annotation.getMetadata().getClassName();
    				// If annName is not of Import type, continue recursion
    				if (!annName.equals(Import.class.getName())) {
    					collectImports(annotation, imports, visited);
    				}
    }
    // Finally add to the imports collection
    imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    Copy the code

  3. Now that you’ve executed the getImports(sourceClass) method, you’ve got two classes:

    1. AutoConfigurationPackages the Registrar of the inner class
    2. AutoConfigurationImportSelector
  4. The AutoConfigurationImportSelector is to realize the automatic assembling of core classes, we continue to execute processImports () method.

    The class diagram

    With the code comments below, we first iterate through the importCandidates collection, which has two values, which are obtained in Step 3. First check whether the ImportSelector interface is implemented, whether the DeferredImportSelector interface is implemented. Don’t worry, we will use a class diagram to clearly show the relationship between these classes.

    According to the class diagram, the first AutoConfigurationPackages the Registrar of the inner class does not implement ImportSelector interface, so do the following logic, we don’t dig in.

    Deferred: Deferred

    While ImportSelector AutoConfigurationImportSelector also implements DeferredImportSelector interface, so join the deferredImportSelectorHandler, delay processing.

  5. Then the @imort annotation parsing is done, and the @importResource, @bean, and so on are parsed. This has nothing to do with autoloing, so ignore it. Resolve these labels, and we will handle deferredImportSelectorHandler, click the process into the method. And then processorGroupImorts()

    / / processing deferredImportSelectorHandler specific process
    this.deferredImportSelectorHandler.process();
    Copy the code

    The process method

    ProcessGruopImports method

6. Click the process method in the getImports() method. After performing getAutoConfigurationEntry returns a AutoConfigurationEntry entrance automatic assembly object. Click on this method. We can find out the most important methods in AutoConfigurationImportSelector getCandidateConfigurations. And then the next step, which was described above. Here we have implemented the process of SpringBoot automatic assembly.

conclusion

In fact, SringBoot is not so magical, its underlying logic is based on Spring, when we understand the implementation process of Spring, whether it is to see SpringBoot, SpringMVC, SpringCloud, SpringSecurity will be handy.