How does Springboot start a web application through run?

content

1. The secret behind SpringBootApplication

1、@Configuration

2、@ComponentScan

3、@EnableAutoConfiguration

The hero behind the automatic configuration: SpringFactoriesLoader in detail

Second, in-depth exploration of the SpringApplication execution process

Overview:    


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

1 @SpringBootApplication
2 public class Application {
3     public static void main(String[] args) {
4         SpringApplication.run(Application.class, args);
5     }
6 }

As can be seen from the above code, the Annotation definition (@SpringBootApplication) and the class definition (SpringApplication.run) are the most dazzling, so to uncover the mystery of SpringBoot, we have to start with these two.

1. The secret behind SpringBootApplication

The @SpringBootApplication annotation is the core annotation of Spring Boot, which is actually a combined annotation:

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 @Inherited
 5 @SpringBootConfiguration
 6 @EnableAutoConfiguration
 7 @ComponentScan(excludeFilters = {
 8         @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
 9         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
10 public @interface SpringBootApplication {
11 ...
12 }

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

i.e. @SpringBootApplication = (default properties) @Configuration + @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:

1 @Configuration
2 @EnableAutoConfiguration
3 @ComponentScan
4 public class Application {
5     public static void main(String[] args) {
6         SpringApplication.run(Application.class, args);
7     }
8 }

It is tiring to write these three each time, so it is convenient to write a @SpringBootApplication. Next, these three Annotations are introduced separately.

1、@Configuration

     The @Configuration here is not unfamiliar to us. It 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 actually a configuration class of an IoC container.

To give a few simple examples to review, the difference between XML and config configuration methods:

(1) The level of expression

The XML-based configuration is like this:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
5        default-lazy-init="true">
6     <!--bean定义-->
7 </beans>

The configuration based on JavaConfig is like this:

1 @Configuration
2 public class MockConfiguration{
3     //bean定义
4 }

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

(2) Registering the bean definition level

The XML-based configuration is of the form:

1 <bean id="mockService" class="..MockServiceImpl">
2     ...
3 </bean>

The configuration based on JavaConfig is like this:

1 @Configuration
2 public class MockConfiguration{
3     @Bean
4     public MockService mockService(){
5         return new MockServiceImpl();
6     }
7 }

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.

(3) Express dependency injection relationship level

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

1 <bean id="mockService" class="..MockServiceImpl">
2     <propery name ="dependencyService" ref="dependencyService" />
3 </bean>
4 
5 <bean id="dependencyService" class="DependencyServiceImpl"></bean>

The configuration based on JavaConfig is like this:

 1 @Configuration
 2 public class MockConfiguration{
 3     @Bean
 4     public MockService mockService(){
 5         return new MockServiceImpl(dependencyService());
 6     }
 7     
 8     @Bean
 9     public DependencyService dependencyService(){
10         return new DependencyServiceImpl();
11     }
12 }

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.


@Configuration: mentioning @Configuration means mentioning his partner @Bean. Using these two annotations, you can create a simple spring configuration class that can be used to replace the corresponding xml configuration file.

1 <beans> 
2     <bean id = "car" class="com.test.Car"> 
3         <property name="wheel" ref = "wheel"></property> 
4     </bean> 
5     <bean id = "wheel" class="com.test.Wheel"></bean> 
6 </beans>

is equivalent to:

 1 @Configuration 
 2 public class Conf { 
 3     @Bean 
 4     public Car car() { 
 5         Car car = new Car(); 
 6         car.setWheel(wheel()); 
 7         return car; 
 8     }
 9     
10     @Bean 
11     public Wheel wheel() { 
12         return new Wheel(); 
13     } 
14 }

The @Configuration annotated class identifies that this class can use the Spring IoC container as the source of bean definitions.

The @Bean annotation tells Spring that a method annotated with @Bean will return an object that should be registered as a bean in the Spring application context.

2、@ComponentScan

     The @ComponentScan annotation 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.

3、@EnableAutoConfiguration

    Personally, I feel that @EnableAutoConfiguration is the most important Annotation, so I will interpret it at the end. Do you still remember the Annotation definitions provided by the Spring framework whose names start with @Enable? For example, @EnableScheduling, @EnableCaching, @EnableMBeanExport, etc., @EnableAutoConfiguration's concept and way of doing things are actually in the same line. A brief summary is that with the support of @Import, bean definitions related to specific scenarios are collected and registered.

  • @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 will automatically configure the project according to the jar dependencies in the classpath, such as: adding spring-boot-starter-web dependencies will automatically add Tomcat and Spring MVC dependencies, and Spring Boot will automatically configure Tomcat and Spring MVC .

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

 1 @SuppressWarnings("deprecation")
 2 @Target(ElementType.TYPE)
 3 @Retention(RetentionPolicy.RUNTIME)
 4 @Documented
 5 @Inherited
 6 @AutoConfigurationPackage
 7 @Import(EnableAutoConfigurationImportSelector.class)
 8 public @interface EnableAutoConfiguration {
 9     ...
10 }

    Among them, the most critical is @Import(EnableAutoConfigurationImportSelector.class). With the help of EnableAutoConfigurationImportSelector, @EnableAutoConfiguration can help SpringBoot applications load all qualified @Configuration configurations into the IoC container created and used by the current SpringBoot. Just like an "octopus", 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 in detail

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.

 1 public abstract class SpringFactoriesLoader {
 2     //...
 3     public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
 4         ...
 5     }
 6 
 7 
 8     public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
 9         ....
10     }
11 }

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

The above picture is an excerpt from the META-INF/spring.factories configuration file in SpringBoot's autoconfigure dependency package, which can illustrate the problem well.

Therefore, the magic knight automatically configured by @EnableAutoConfiguration 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) is instantiated as the corresponding IoC container configuration class in the form of JavaConfig marked with @Configuration, and then aggregated into one and loaded into the IoC container.

Second, 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 the web application.
  • 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)

After removing the event notification point, the whole process is as follows:


This article takes debugging an actual SpringBoot startup program as an example, and refers to the main class diagram in the process to analyze its startup logic and automatic configuration principle.

Overview:    

    The above picture shows the SpringBoot startup structure diagram. We found that the startup process is mainly divided into three parts. The first part is the initialization module of SpringApplication, and some basic environment variables, resources, constructors, and listeners are configured. The second part realizes the application specific The startup scheme includes the monitoring module of the startup process, the loading configuration environment module, and the core creation context environment module. The third part is the automatic configuration module, which is the core of springboot automatic configuration, which will be discussed in detail in the following analysis. In the starter below we will chain together the main functions in the structure.

start up:

     Each SpringBoot program has a main entry, which is the main method. SpringApplication.run() is called in main to start the entire spring-boot program. The class where this method is located needs to use the @SpringBootApplication annotation, as well as the @ImportResource annotation (if need), @ SpringBootApplication includes three annotations with the following functions:

@EnableAutoConfiguration: SpringBoot automatically configures the Spring framework based on the dependencies declared by the application.

@SpringBootConfiguration (internally @Configuration): The marked class is equal to the spring XML configuration file (applicationContext.xml), which assembles all bean transactions and provides a spring context.

@ComponentScan: Component scanning, which can automatically discover and assemble beans. By default, the files in the package path of Booter.class in the run method of SpringApplication are scanned, so it is best to put the startup class in the root package path.

SpringBoot startup class

First enter the run method

A SpringApplication instance is created in the run method. In this constructor, we can find that it calls an initialized initialize method.

Here is mainly to assign some initial values ​​to the SpringApplication object. After the constructor is executed, we return to the run method

The following key steps are implemented in this method:

1. Created the application's listener SpringApplicationRunListeners and started listening

2. Load the SpringBoot configuration environment (ConfigurableEnvironment), if it is published through the web container, it will load the StandardEnvironment, which finally inherits the ConfigurableEnvironment, the class diagram is as follows

It can be seen that *Environment finally implements the PropertyResolver interface. When we usually obtain the value method corresponding to the Key specified in the configuration file through the environment object, we call the getProperty method of the propertyResolver interface.

3. The configuration environment (Environment) is added to the listener object (SpringApplicationRunListeners)

4. Create the return object of the run method: ConfigurableApplicationContext (application configuration context), we can take a look at the creation method:

The method will first obtain the explicitly set application context (applicationContextClass), if it does not exist, then load the default environment configuration (judged by whether it is a web environment), select the AnnotationConfigApplicationContext annotation context by default (by scanning all annotation classes to load beans), and finally Instantiate the context object through BeanUtils and return it.

The ConfigurableApplicationContext class diagram is as follows:

It mainly depends on two directions of its inheritance:

LifeCycle: life cycle class, which defines start start, stop end, isRunning whether to run the medium life cycle null value method

ApplicationContext: Application context class, which mainly inherits beanFactory (bean factory class)

5. Back in the run method, the prepareContext method associates important components such as listeners, environment, applicationArguments, and banners with the context object

6. The next refreshContext(context) method (the initialization method is as follows) will be the key to realize the automatic configuration of spring-boot-starter-* (mybatis, redis, etc.), including the loading of spring.factories, the instantiation of beans and other core tasks .

     After the configuration is complete, Springboot does some basic finishing work and returns the application environment context. Looking back at the overall process, the startup of Springboot mainly creates a configuration environment (environment), event listeners (listeners), and application context (applicationContext), and based on the above conditions, starts to instantiate the beans we need in the container, so far, through SpringBoot startup The program has been constructed, let's discuss how the automatic configuration is implemented.


Automated configuration:

In the previous startup structure diagram, we noticed that both the application initialization and the specific execution process call the SpringBoot auto-configuration module.

SpringBoot auto-configuration module

    The main use of this configuration module is SpringFactoriesLoader, that is, the Spring factory loader. This object provides the loadFactoryNames method. The input parameters are factoryClass and classLoader, that is, the factory class name and the corresponding class loader in the above figure need to be passed in. The method will be based on The specified classLoader loads the specified file under the search path of the class adder, that is, the spring.factories file. The incoming factory class is the interface, and the corresponding class in the file is the implementation class of the interface, or finally as the implementation class, so The file is generally a one-to-many collection of class names as shown in the figure below. After obtaining the class names of these implementation classes, the loadFactoryNames method returns the collection of class names. After the method caller obtains these collections, it obtains the class objects of these classes through reflection. , construction method, and finally generate an instance.

Factory interface and some of its implementation class interface names

The following diagram helps us visualize the automatic configuration process.

SpringBoot automatic configuration key component diagram 

    The META-INF files of components such as mybatis-spring-boot-starter and spring-boot-starter-web all contain spring.factories files. In the automatic configuration module, SpringFactoriesLoader collects the full class name in the file and returns a full class name The returned full name of the class is instantiated through reflection to form a specific factory instance, and the factory instance is used to generate the beans required by the component.

Before we mentioned the EnableAutoConfiguration annotation, its class diagram is as follows:

It can be found that it finally implements ImportSelector (selector) and BeanClassLoaderAware (bean class loader middleware), focusing on the selectImports method of AutoConfigurationImportSelector.

    This method is executed before the springboot startup process - bean instantiation, and returns a list of class information to be instantiated. We know that if the class information is obtained, spring can naturally load the class into the jvm through the class loader. Now we have depended on the components we need through the starter dependency of spring-boot, then the class information of these components is in the select method. can also be obtained, don't worry, we continue to analyze down.

The getCandidateConfigurations method in this method, learned through the method annotation, returns a list of the class names of the auto-configuration classes, the method calls the loadFactoryNames method, check the method

    In the above code, you can see that the auto configurator will find the corresponding key in all the spring.factories files under the project system path according to the incoming factoryClass.getName(), so as to load the classes inside. We will select the spring.factories file under this mybatis-spring-boot-autoconfigure

Enter org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration, mainly look at the class header:

I found that Spring's @Configuration is like a springBean annotated by annotations, and continue to look down,

@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class}) This annotation means: MybatisAutoConfiguration configuration class is parsed only when SqlSessionFactory.class and SqlSessionFactoryBean.class exist, otherwise this configuration class will not be parsed, make sence, We need mybatis to return the session object for us, so there must be a session factory related class.

@CondtionalOnBean(DataSource.class): Only handles dataSources that have been declared as beans.

@ConditionalOnMissingBean(MapperFactoryBean.class) This annotation means that if the bean specified by name does not exist in the container, bean injection will be created, otherwise it will not be executed (the source code of this class is long, and the space limit is not fully pasted)

     The above configuration can ensure that the components required by mybatis such as sqlSessionFactory, sqlSessionTemplate, and dataSource can be automatically configured. The @Configuration annotation has provided the context of Spring, so the configuration of the above components is the same as the configuration of the mybatis.xml file when Spring starts. to an effect. Through analysis, we can find that as long as there are SqlSessionFactory.class, SqlSessionFactoryBean.class in the classpath based on a SpringBoot project, and the dataSourceBean has been registered in the container, the automatic configuration can be triggered, which means that we only need to add mybatis to the maven project. The required dependencies can trigger automatic configuration, but if the native dependencies of mybatis are introduced, the automatic configuration class must be modified for each integrated function, and the out-of-the-box effect will not be obtained. So Spring-boot provides us with a unified starter that can directly configure related classes, and the dependencies (mybatis) required to trigger automatic configuration are as follows:

Here are all the dependencies in the pom.xml file in the source code of mybatis-spring-boot-starter intercepted:

 Because of the transitivity of maven dependencies, we can rely on all the classes that need to be automatically configured as long as we rely on starter to achieve out-of-the-box functions. It also reflects that Springboot simplifies a large number of XML configurations and complex dependency management brought by the Spring framework, allowing developers to pay more attention to the development of business logic.

Good article, please  follow me to  favorite this article 

Guess you like

Origin blog.csdn.net/weixin_44302240/article/details/123789958