You must know the principle of SpringBoot startup configuration!

Using Spring Boot

First open IDEA to create a Spring Boot project

Select SpringInitializer and then the next process is not cumbersome.

Then open the pom file and we found that Srping Boot has a configuration dependency

xml复制代码启动依赖
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

测试依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        

After clicking the startup dependency, we found that there are various dependencies. The
original startup dependency includes spring-boot-starter startup dependency, mvc dependency, tomcat and json conversion package, and validator verification package, etc.

xml复制代码<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>
	</dependencies>

Then there is a main program in the root directory, the startup class

java复制代码@SpringBootApplication
public class SpringDemoApplication {

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

}

If the main startup class is not marked by @SpringBootApplication, an error will be reported at startup: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

@SpringBootApplication startup annotation

After clicking in, I found that this is a combined annotation

java复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
 ....
 }

Found that there are three annotations @SpringBootConfiguration
@EnableAutoConfiguration @ComponentScan

It can be explained that the @SpringBootApplication annotation is a combined annotation.
We can also use @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan on the startup class to start Spring Boot

Let's first look at the @ComponentScan annotation

java复制代码@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

The function of this annotation is to scan the path under the specified package, specifying two filter conditions TypeExcludeFilter and AutoConfigurationExcludeFilter

These two should filter some packages at startup or add some components to the container at startup

There is a match method in the source code of the TypeExcludeFilter class to judge the logic of filtering

kotlin复制代码    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        if (this.beanFactory instanceof ListableBeanFactory && this.getClass().equals(TypeExcludeFilter.class)) {
            Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
            Iterator var4 = delegates.iterator();

            while(var4.hasNext()) {
                TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
                if (delegate.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }

        return false;
    }

if (this.beanFactory instanceof ListableBeanFactory This sentence can see the underlying Ioc container of BeanFactory to execute the custom filtering method TypeExcludeFilter is used for extended component filtering

Similarly, there is also a match method in AutoConfigurationExcludeFilter. You can see this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader) in the source code to judge whether it is configuration and automatic configuration, so don't pay attention for now.

java复制代码

public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
    private ClassLoader beanClassLoader;
    private volatile List<String> autoConfigurations;

    public AutoConfigurationExcludeFilter() {
    }

    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
    }

    private boolean isConfiguration(MetadataReader metadataReader) {
        return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
    }

    private boolean isAutoConfiguration(MetadataReader metadataReader) {
        return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
    }

    protected List<String> getAutoConfigurations() {
        if (this.autoConfigurations == null) {
            this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
        }

        return this.autoConfigurations;
    }
}

Then look at the @EnableAutoConfiguration annotation

java复制代码@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

You can see that there are @AutoConfigurationPackage and
@Import({EnableAutoConfigurationImportSelector.class})

These two annotations

First look at the @AutoConfigurationPackage annotation. From the name, it is an automatic configuration package. After clicking in,
@Import({Registrar.class}) public @interface AutoConfigurationPackage { } finds that @Import({Registrar.class}) imports a component for the @ annotation.

Registrar component

java复制代码   @Order(-2147483648)
   static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
       Registrar() {
       }

       public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
           AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
       }

       public Set<Object> determineImports(AnnotationMetadata metadata) {
           return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
       }
   }

After debugging, you can see that the function of this annotation is to import the package name under our root directory, and import components through the package name, that is, scan various beans and register them in the Ioc container.

Then let's study @Import({EnableAutoConfigurationImportSelector.class})
It seems that this is also an import component
Open the EnableAutoConfigurationImportSelector class

java复制代码public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector

Then open AutoConfigurationImportSelector and you will see the following code

java复制代码public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
 

This class implements the DeferredImportSelector interface and you will find that this class inherits the ImportSelector class

java复制代码public interface DeferredImportSelector extends ImportSelector 

The function is that the execution time of DeferredImportSelector is later than that of ImportSelector, and after importing the component, it will search for the result of the imported component.

Then we also found that the following code selectImports method looks for imported components by name, which also proves the significance of AutoConfigurationImportSelector inheriting the DeferredImportSelector interface.

java复制代码    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            try {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                configurations = this.sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return (String[])configurations.toArray(new String[configurations.size()]);
            } catch (IOException var6) {
                throw new IllegalStateException(var6);
            }
        }
    }

You can see the following code

java复制代码List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

It seems that a configuration collection is configured

After clicking in, I found the following code

java复制代码SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

this.getSpringFactoriesLoaderFactoryClass()

java复制代码 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

The incoming Class is @EnableAutoConfiguration, and in this class
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; enable automatic configuration

SpringFactoriesLoader.loadFactoryNames scans the classpath

Use classLoader to load resources under the specified constant path META-INF/spring.factories

We will find that spring.factories files are loaded under multiple packages

Then debug continues to go down and finds

In the selectImports method of the AutoConfigurationImportSelector class, configurations = this.filter(configurations, autoConfigurationMetadata); this code performs filter conditions

The incoming parameter autoConfigurationMetadata is the above code AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); The automatic configuration condition query results of all automatic configuration classes

Click in and find that all the spring-autoconfigure-metadata.properties files are loaded,
and then find that this file basically has a full path package name starting with org.springframework.boot.autoconfigure, and I understand it instantly

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
This class is to find all the automatic configuration classes supported by Spring Boot

Then filter whether the loaded automatic configuration package contains imports under the class path

AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next(); Automatic configuration import filter

Exclude the automatic configuration conditions that do not exist in the class path, and finally add 20 default automatic configurations to the Ioc container

Summarize

Basically, the automatic configuration principle of Spring Boot is roughly divided into three steps:

  • 1 The filter first filters on the startup class or adds components to the container
  • 2 Scan all beans under the root path
  • 3 Add the class path META-INF/spring.factories and META-INF/spring-autoconfigure-metadata.properties automatically configured packages and filters whether the automatic configuration has packages under the class path and finally registered in Ioc

Guess you like

Origin blog.csdn.net/m0_71777195/article/details/130721670