Analysis of Spring Boot startup principle

When we develop a Spring Boot project, we will use the following startup classes

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

Annotation definition (@SpringBootApplication) and class definition (SpringApplication.run) are the core.

 

--- SpringBootApplication ---

@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 {
...

Although the definition uses multiple Annotations to label the original information, there are actually only three Annotations that are important:

  • @Configuration (@SpringBootConfiguration click to view and find that @Configuration is still applied)
  • @EnableAutoConfiguration
  • @ComponentScan

Therefore, if we use the following SpringBoot startup class, the entire SpringBoot application can still be functionally equivalent to the previous startup class:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

 

--- @Configuration ---

@Configuration is the @Configuration used by the configuration class of the Spring Ioc container in the form of JavaConfig. The SpringBoot community recommends using the configuration form based on JavaConfig. Therefore, after the startup class here is marked with @Configuration, it is also a configuration class of the IoC container.

The difference between XML and config configuration

expression level

The XML-based configuration is like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">
    <!--bean definition-->
</beans>

The configuration based on JavaConfig is like this:

@Configuration
public class MockConfiguration{
    //bean definition
}

Any Java class definition annotated with @Configuration is a JavaConfig configuration class.

 

Register the bean definition level

The XML-based configuration is of the form:

<bean id="mockService" class="..MockServiceImpl">
    ...
</bean>

The configuration based on JavaConfig is like this:

@Configuration
public class MockConfiguration{
    @Bean
    public MockService mockService(){
        return new MockServiceImpl();
    }
}

For any method marked with @Bean, its return value will be registered in Spring's IoC container as a bean definition, and the method name will default to the id of the bean definition.

 

Express dependency injection relationship level

In order to express the dependencies between beans and beans, it is generally like this in XML form:

<bean id="mockService" class="..MockServiceImpl">
    <propery name ="dependencyService" ref="dependencyService" />
</bean>

<bean id="dependencyService" class="DependencyServiceImpl"></bean>

The configuration based on JavaConfig is like this:

@Configuration
public class MockConfiguration{
    @Bean
    public MockService mockService(){
        return new MockServiceImpl(dependencyService());
    }
    
    @Bean
    public DependencyService dependencyService(){
        return new DependencyServiceImpl();
    }
}

If the definition of a bean depends on other beans, you can directly call the creation method of the dependent bean in the corresponding JavaConfig class.

 

--- @ComponentScan ---

The annotation @ComponentScan is very important in Spring. It corresponds to the elements in the XML configuration. The function of @ComponentScan is to automatically scan and load qualified components (such as @Component and @Repository, etc.) or bean definitions, and finally define these beans. Loaded into the IoC container.

We can fine-grainly customize the scope of @ComponentScan automatic scanning through attributes such as basePackages. If not specified, the default Spring framework implementation will scan from the package that declares the class where @ComponentScan is located.

Note: So SpringBoot's startup class is best placed under the root package, because basePackages is not specified by default.

 

--- @EnableAutoConfiguration ---

A brief summary of @EnableAutoConfiguration is to collect and register bean definitions related to specific scenarios with the support of @Import .

In the Spring framework, annotations starting with @Enable are:

  • @EnableScheduling loads Spring scheduling framework-related bean definitions into the IoC container through @Import.
  • @EnableMBeanExport is to load JMX related bean definitions to the IoC container through @Import.

And @EnableAutoConfiguration also uses the help of @Import to load all bean definitions that meet the auto-configuration conditions into the IoC container, that's all!

@EnableAutoConfiguration, as a composite Annotation, defines its own key information as follows:

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

Among them, the most critical one is @Import(EnableAutoConfigurationImportSelector.class) . With the help of EnableAutoConfigurationImportSelector, @EnableAutoConfiguration can help SpringBoot applications load all eligible @Configuration configurations into the current IoC container created and used by SpringBoot.

With the support of an original tool class of the Spring framework: SpringFactoriesLoader, @EnableAutoConfiguration can automatically configure the function intelligently and you are done!

 

The hero behind the automatic configuration: SpringFactoriesLoader details
SpringFactoriesLoader is a private extension scheme of the Spring framework. Its main function is to load the configuration from the specified configuration file META-INF/spring.factories.

public abstract class SpringFactoriesLoader {
    //...
    public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
        ...
    }


    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        ....
    }
}

When used with @EnableAutoConfiguration, it provides more support for configuration lookup, that is, according to the complete class name of @EnableAutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration as the key to look up, get a corresponding set of @Configuration classes

 

Therefore, @EnableAutoConfiguration automatic configuration becomes: search all META-INF/spring.factories configuration files from the classpath, and pass the configuration items corresponding to org.springframework.boot.autoconfigure.EnableutoConfiguration through reflection (Java Refletion) instance It is converted into a corresponding IoC container configuration class in the form of JavaConfig marked with @Configuration, and then aggregated into one and loaded into the IoC container.

 

--- In-depth exploration of the SpringApplication execution process ---

The implementation of SpringApplication's run method is the main route of our journey. The main process of this method can be roughly summarized as follows:

1) If we are using the static run method of SpringApplication, then this method first creates an instance of the SpringApplication object, and then calls the instance method of the created SpringApplication. When the SpringApplication instance is initialized, it will do several things in advance:

  • Depending on whether there is a characteristic class (org.springframework.web.context.ConfigurableWebApplicationContext) in the classpath, it is decided whether to create an ApplicationContext type used by Web applications.
  • Use SpringFactoriesLoader to find and load all available ApplicationContextInitializers on the application's classpath.
  • Use SpringFactoriesLoader to find and load all available ApplicationListeners in the application's classpath.
  • Infer and set the defining class of the main method.

2) After the SpringApplication instance is initialized and the settings are completed, the logic of the run method is executed. At the beginning of the method execution, all the SpringApplicationRunListeners that can be found and loaded through the SpringFactoriesLoader are traversed and executed. Calling their started() method tells these SpringApplicationRunListeners, "Hey, the SpringBoot application is about to start executing!".

3) Create and configure the Environment that the current Spring Boot application will use (including configuring the PropertySource and Profile to be used).

4) Traverse the environmentPrepared() method of calling all SpringApplicationRunListener and tell them: "The Environment used by the current SpringBoot application is ready!".

5) If the SpringApplication's showBanner property is set to true, print the banner.

6) According to whether the user has explicitly set the applicationContextClass type and the inference result of the initialization phase, decide what type of ApplicationContext to create for the current SpringBoot application and complete the creation, and then decide whether to add ShutdownHook according to conditions, decide whether to use a custom BeanNameGenerator, and decide whether to Use a custom ResourceLoader, of course, the most important thing, set the previously prepared Environment to the created ApplicationContext.

7) After the ApplicationContext is created, SpringApplication will use Spring-FactoriesLoader again to find and load all available ApplicationContext-Initializers in the classpath, and then traverse and call the initialize (applicationContext) method of these ApplicationContextInitializers to further process the created ApplicationContext.

8) Traverse the contextPrepared() method of all SpringApplicationRunListener calls.

9) The core step is to load all the configurations obtained through @EnableAutoConfiguration and other forms of IoC container configurations into the prepared ApplicationContext.

10) Traverse the contextLoaded() method of all SpringApplicationRunListener calls.

11) Call the refresh() method of the ApplicationContext to complete the last process available to the IoC container.

12) Find out whether CommandLineRunner is registered in the current ApplicationContext, and if so, traverse and execute them.

13) Under normal circumstances, traverse and execute the finished() method of SpringApplicationRunListener, (if the whole process is abnormal, the finished() method of all SpringApplicationRunListener will still be called, but in this case, the exception information will be passed in for processing)
to remove After the event notification point, the whole process is as follows:



 

 

http://tengj.top/2017/03/09/springboot3/#

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326446701&siteId=291194637