Spring Boot automatic assembly (detailed explanation of necessary problems)

Posted by jjbarnone on Sat, 05 Feb 2022 12:16:01 +0100

Based on Spring Boot 2.6.3, this paper summarizes the core points of interview answers at the end of the article.

1. What is automatic assembly

When we use Spring Boot, we will automatically assemble the Bean into the IoC container. For example, when using Redis database, we will introduce the dependency Spring Boot starter data Redis. After this dependency is introduced, the components required for Redis operation will be injected into the IoC container for subsequent use during service initialization. The general process of automatic assembly is as follows:

(1) Get the spring. INF file under the META-INF folder of the component (for example, spring boot starter data redis) Factories file.
(2)spring. The factories file lists the classes that need to be injected into the IoC container.
(3) Inject the entity class into the IoC container for use.

2. Automatic assembly principle

The general process in the previous chapter is implemented through @ SpringBootApplication. This annotation is declared on the startup class of Spring Boot.

@SpringBootApplication
public class DailySpringApplication {
    public static void main(String[] args) {
        SpringApplication.run(DailySpringApplication.class, args);
    }
}

Click the @ bootconfiguration annotation to enter the @ bootconfiguration annotation automatically.

@Target({ElementType.TYPE}) // This annotation applies to interfaces, classes, and enumerations
@Retention(RetentionPolicy.RUNTIME) // The annotation is not only saved in the class file, but also exists after the jvm loads the class file
@Documented // After having the annotation, if an interface uses the annotation, the annotation will be displayed in the generated javadoc file
@Inherited // If a class is annotated with the @ Inherited modifier, its subclasses will inherit the annotation
@SpringBootConfiguration // Identifies that it is a Spring Boot configuration class
@EnableAutoConfiguration // This annotation is mainly used to realize automatic assembly
@ComponentScan(  // Add @ ComponentScan annotation on the configuration class. This annotation will scan all configuration classes under the package of this class by default
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

Click to enter the @ EnableAutoConfiguration annotation. The function of AutoConfigurationPackage is to record the package of the class using the annotation and the path of the sub package for subsequent reading. You can refer to this article article . Among them, the most important is autoconfigurationimportselector Class, assemble the class to be assembled into the IoC container. Let's focus on the implementation of this class.

@Target({ElementType.TYPE}) // This annotation applies to interfaces, classes, and enumerations
@Retention(RetentionPolicy.RUNTIME) // The annotation is not only saved in the class file, but also exists after the jvm loads the class file
@Documented // After having the annotation, if an interface uses the annotation, the annotation will be displayed in the generated javadoc file
@Inherited // If a class is annotated with the @ Inherited modifier, its subclasses will inherit the annotation
@AutoConfigurationPackage // Record the package where the class using this annotation is located and the path of the sub package for subsequent reading
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration{...}

3. Core category analysis

selectImport in AutoConfigurationImportSelector is the core implementation of automatic assembly. It mainly reads meta-inf / spring After de duplication and filtering, the factories file returns the set of configuration classes to be assembled.

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

Let's click the getAutoConfigurationEntry() method:

  • getAttributes get exclude, excludeName, etc. in @ EnableAutoConfiguration.
  • getCandidateConfigurations gets all auto assembled configuration classes, that is, reading spring Factories file, which will be explained again later.
  • Removedduplicates removes duplicate configuration items.
  • getExclusions removes unnecessary configuration classes according to exclude and excludeName in @ EnableAutoConfiguration.
  • fireAutoConfigurationImportEvents broadcast events

Finally, the configuration class collection is returned according to multiple filtering and judgment.

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata); 
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

We click getCandidateConfigurations() method. Here, we scan meta-inf / spring. Inf under classpath through loadFactoryNames method The factories file is stored in the form of key=value. We read that key=EnableAutoConfiguration. Value is the configuration class to be assembled, that is, the value returned by getCandidateConfigurations.

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
     List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
     Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
     return configurations;
}

4. Core summary

(1) Automatic assembly is realized by annotation @ springbootapplication = > @ enableautoconfiguration = > @ import ({autoconfigurationimportselector. Class}).
(2) The AutoConfigurationImportSelector class overrides the selectImports method in ImportSelector to return the configuration classes to be assembled in batches.
(3) Scan meta-inf / Spring.exe under the classpath through the Spring factoryesloader mechanism provided by Spring Factories file to read the configuration classes that need to be automatically assembled.
(4) The unqualified configuration classes are removed according to the condition screening method, and the automatic assembly is finally completed.

Ref:
1. Principle and practice of Spring Cloud Alibaba micro service Tan Feng
2. Core principles of Spring 5 and handwriting practice of 30 classes by Tan Yongde

Topics: Java Spring Spring Boot