You don't know how SpringBoot boot works!

Startup principle of SpringBoot

@[toc]

Use Spring Boot

First open IDEA to create a Spring Boot project

Select SpringInitializer and the next process is not cumbersome.

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

Startup Dependency
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

Test Dependency
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        

After clicking Startup Dependencies we find all kinds of dependencies The original startup dependencies included spring-boot-starter startup dependencies, mvc dependencies, tomcat and json conversion packages, validator validation packages, and so on.

<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's a main program in the root directory that starts the class

@SpringBootApplication
public class SpringDemoApplication {

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

}

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

@SpringBootApplication Startup Comment

When the point goes in, it's a combination note


@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 @SpringBootConfiguration @EnableAutoConfiguration Three notes like @ComponentScan

You can say that the @SpringBootApplication annotation is a combination annotation We can also start Spring Boot using @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan on the startup class

Start with the @ComponentScan annotation

@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

This annotation scans the path under the specified package, specifying two filter conditions, TypeExcludeFilter and AutoConfiguration ExcludeFilter

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

The TypeExcludeFilter class source has a match ing method for filtering logic

    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 instances of ListableBeanFactory) you can see the Ioc container at the bottom of the BeanFactory to perform the custom filtering method TypeExcludeFilter for extended component filtering

Similarly, there is a match method in the AutoConfiguration ExcludeFilter that allows you to see if this.isConfiguration (metadata Reader) & this.isAutoConfiguration (metadata Reader) in the source code determines whether it is configured and auto-configured, without paying attention to it for the time being.



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 comment

@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 @AutoConfiguration Package and @Import({EnableAutoConfigurationImportSelector.class})

These two notes

First look at the @AutoConfiguration Package annotation. By name, it is an auto-configuration package. Click in @Import({Registrar.class}) Public @interface AutoConfiguration Package {} found importing a component @Import({Registrar.class}) to the @annotation

Registrar Component

   @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));
       }
   }

Deug can see that this annotation imports the package name under our root directory, imports components through the package name, that is, scans the bean s and registers them in the Ioc container.

Then look at @Import ({EnableAutoConfiguration ImportSelector.class}) It looks like this is also an import component Open EnableAutoConfigurationImportSelector class

public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector

Open AutoConfiguration ImportSelector again and you will see the following code

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

public interface DeferredImportSelector extends ImportSelector 

The effect is that DeferredImportSelector executes later than ImportSelector and looks for imported component results after importing the component.

We then found the following code, selectImports, which looks by name for imported components, which also proves the meaning of AutoConfigurationImportSelector inheriting the DeferredImportSelector interface.

    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

List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

It looks like a collection of configurations has been configured

The following code is found when the point goes in

SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

this.getSpringFactoriesLoaderFactoryClass()

 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

The Class passed in is @EnableAutoConfiguration, and in this class String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; turn on autoconfiguration

SpringFactoriesLoader.loadFactoryNames scanned the class path

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

We will find that the spring.factories file is loaded under several packages

Then debug goes down and finds out

Configurations = this.filter (configurations, autoConfiguration Metadata) in the selectImportSelector method in the AutoConfiguration ImportSelector class; this code is filtered

The parameter autoConfiguration Metadata passed in is AutoConfiguration Metadata autoConfiguration Metadata = AutoConfiguration Metadata Loader.loadMetadata (this.beanClassLoader); the query result

Point in found that all spring-autoconfigure-metadata.properties files were loaded Then I found that this file is basically full path package name starting with org.springframework.boot.autoconfigure, and I understood it in a flash

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); This class is the one that finds all Spring Boot-supported autoconfigurations

Then filter if the loaded auto-configuration package contains imports in the class path

AutoConfiguration ImportFilter filter = (AutoConfiguration ImportFilter) var8.next(); automatically configure import filter

Exclude autoconfigurations that do not exist under the class path and finally add 20 default autoconfigurations to the Ioc container

summary

Basically, Spring Boot's auto-configuration principle is roughly divided into three steps:

  • 1 Filter first filters on the startup class or adds components to the container
  • 2 Scan all bean s in the root path
  • 3Add packages and filter autoconfigurations for class paths META-INF/spring.factories and META-INF/spring-autoconfigure-metadata.properties Autoconfigurations Whether packages under class paths are last registered in Ioc

Pay attention to me

Long by 2-D code

If you like this article, like it, read it, forward it.

Tags: Spring Tomcat Hibernate SpringBoot

Posted on Mon, 16 Mar 2020 12:44:26 -0400 by cyandi_man