Detailed Springboot startup principle analysis

Insert picture description here
We develop any Spring Boot project, we will use the following startup class

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

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

One, the secret behind SpringBootApplication

@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:
@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 mark the original information, only three Annotations are actually important:

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

That is, @SpringBootApplication =
(default property) @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:

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

It is tiring to write these 3 each time, so it is convenient to write a @SpringBootApplication. Next, we will introduce these 3 Annotations respectively.

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, the startup class here is marked with @Configuration. It is actually a configuration class of an IoC container.

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

(1) Expression level

The way based on XML 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定义-->
</beans>

The configuration method based on JavaConfig is like this:

@Configuration
public class MockConfiguration{
    
    
    //bean定义
}

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

(2) Register the bean definition level

The XML-based configuration form is like this:

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

The configuration form based on JavaConfig looks 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 as a bean definition in Spring's IoC container, and the method name will default to the id of the bean definition.

(3) Express dependency injection relationship level

In order to express the dependency between bean and bean, 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 form based on JavaConfig looks 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, just call the creation method of the dependent bean in the corresponding JavaConfig class directly.

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

<beans> 
    <bean id = "car" class="com.test.Car"> 
        <property name="wheel" ref = "wheel"></property> 
    </bean> 
    <bean id = "wheel" class="com.test.Wheel"></bean> 
</beans>

Is equivalent to:

@Configuration 
public class Conf {
    
     
    @Bean 
    public Car car() {
    
     
        Car car = new Car(); 
        car.setWheel(wheel()); 
        return car; 
    }

    @Bean 
    public Wheel wheel() {
    
     
        return new Wheel(); 
    } 
}

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

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

2、@ComponentScan

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

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

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

3、@EnableAutoConfiguration

I personally feel that @EnableAutoConfiguration is the most important Annotation. Do you still remember the Annotation definitions that start with @Enable provided by the Spring framework?
For example, @EnableScheduling,
@EnableCaching , @EnableMBeanExport, etc., @EnableAutoConfiguration 's philosophy and way of doing things are actually in the same line. A brief summary is to collect and register bean definitions related to specific scenarios with the support of @Import.

@EnableScheduling loads all bean definitions related to the Spring scheduling framework to the IoC container through @Import.

@EnableMBeanExport is to load JMX related bean definitions into the IoC container through @Import.

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

@EnableAutoConfiguration will automatically configure the project according to the jar dependencies in the classpath. For example, if the spring-boot-starter-web dependency is added, Tomcat and Spring MVC dependencies will be automatically added, and Spring Boot will automatically configure Tomcat and Spring MVC .

Insert picture description here
@EnableAutoConfiguration, as a composite Annotation, defines the 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. Just like an "octopus", with the support of an original tool class of the Spring framework: SpringFactoriesLoader, @EnableAutoConfiguration can intelligently automatically configure the function to be successful!
Insert picture description here
The hero behind the automatic configuration: SpringFactoriesLoader explains
SpringFactoriesLoader is a private extension of the Spring framework. Its main function is to load 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 is more to provide a configuration search function support, that is, according to the full class name of @EnableAutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration as the key to be searched, a set of corresponding @Configuration classes are obtained.

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

Therefore, the magic knight of @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) is instantiated into 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.

Two, 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, in this method, we must first create a SpringApplication object instance, and then call the created SpringApplication instance method. When the SpringApplication instance is initialized, it will do several things in advance:

  • According to whether there is a feature class org.springframework.web.context.ConfigurableWebApplicationContext in the classpath, it is determined whether an ApplicationContext type for web applications should be created.
  • Use SpringFactoriesLoader to find and load all available ApplicationContextInitializers in the application's classpath.
  • Use SpringFactoriesLoader to find and load all available ApplicationListeners in the application classpath.
  • Infer and set the definition class of the main method.

(2) After the initialization of the SpringApplication instance is completed 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 by SpringFactoriesLoader are traversed and loaded. Call their started() method to tell these SpringApplicationRunListener, "Hey, the SpringBoot application is about to start execution!".

(3) Create and configure the Environment to be used by the current Spring Boot application (including the PropertySource and Profile to be configured).

(4) Traverse the environmentPrepared() methods that call all SpringApplicationRunListener and tell them: "The environment used by the current SpringBoot application is ready!".

(5) If the showBanner property of SpringApplication is set to true, the banner is printed.

(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 should be created for the current SpringBoot application and create it, and then decide whether to add ShutdownHook according to the conditions, decide whether to use a custom BeanNameGenerator, and decide Whether to use a custom ResourceLoader, of course, the most important thing is to set the previously prepared Environment to the created ApplicationContext.

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

(8) Traverse and call all the contextPrepared() methods of SpringApplicationRunListener.

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

(10) Traverse and call all the contextLoaded() methods of SpringApplicationRunListener.

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

(12) Find out if there are CommandLineRunners 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 is still 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:
Insert picture description here
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 principles.

Insert picture description here
Overview:

The above picture is 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 configures some basic environment variables, resources, constructors, and listeners;
  • The second part implements the specific startup plan of the application, including 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 following startup procedure we will connect the main functions of the structure in series.

start up:

Each SpringBoot program has a main entrance, that is, the main method. In main, SpringApplication.run() is called to start the entire spring-boot program. The class where the method is located needs to be annotated with @SpringBootApplication, and @ImportResource (if need), @ SpringBootApplication includes three annotations, the functions are as follows:

  • @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 environment.
  • @ComponentScan: Component scanning, which can automatically discover and assemble Beans. By default, the file in the package path of Booter.class in the run method of SpringApplication is scanned, so it is best to put the startup class in the root package path.

Insert picture description here
SpringBoot startup class

First enter the run method: In the
Insert picture description here
run method, a SpringApplication instance is created. In the construction method, we can find that it has called an initialized initialize method.

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

Insert picture description here
The following key steps are implemented in this method:
1. Create the application listener SpringApplicationRunListeners and start listening

2. Load the SpringBoot configuration environment (ConfigurableEnvironment), if it is published through the web container, it will load the StandardEnvironment, which eventually inherits the ConfigurableEnvironment, the class diagram is as follows:
Insert picture description here

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

3. Configure the environment (Environment) to join the listener object (SpringApplicationRunListeners)

4. Create the return object of the run method: ConfigurableApplicationContext (application configuration context), we can look at the creation method: the
Insert picture description here
method will first obtain the explicitly set application context (applicationContextClass), if it does not exist, then load the default environment configuration (through whether It is a web environment judgment), the AnnotationConfigApplicationContext annotation context is selected by default (by scanning all annotation classes to load the bean), and finally the context object is instantiated through BeanUtils and returned.

The ConfigurableApplicationContext class diagram is as follows:

Insert picture description here
It mainly depends on the two directions of its inheritance:

  • LifeCycle: Life cycle class, which defines whether start starts, stop ends, and isRunning is running a medium life cycle null value method
  • ApplicationContext: Application context class, which mainly inherits beanFactory (bean factory class)

5. Back to the run method, the prepareContext method associates important components such as listeners, environment, applicationArguments, and banner 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 .

Insert picture description here
After the configuration, Springboot did some basic finishing work and returned the application environment context. Recalling the overall process, the startup of Springboot mainly creates a configuration environment (environment), event listeners (listeners), and application context (applicationContext). Based on the above conditions, we start to instantiate the beans we need in the container. So far, we start it through SpringBoot. The program has been constructed, let's discuss how to realize the automatic configuration.

Automatic configuration:

In the previous startup structure diagram, we noticed that both the application initialization and the specific execution process have invoked the SpringBoot automatic configuration module.
Insert picture description here
SpringBoot automatic configuration module

The configuration module mainly uses SpringFactoriesLoader, the Spring factory loader. The object provides the loadFactoryNames method. The input parameters are factoryClass and classLoader, which means that 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 factory class passed in 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 a collection of class names. After the method caller obtains these collections, the class objects of these classes are obtained through reflection. , Construction method, and finally generate an instance.
Insert picture description here
Factory interface and its several implementation class interface names

The following figure helps us understand the automatic configuration process.
Insert picture description here
SpringBoot automatic configuration key component relationship diagram

The META-INF files of mybatis-spring-boot-starter, spring-boot-starter-web and other components all contain the spring.factories file. In the automatic configuration module, SpringFactoriesLoader collects the full name of the class in the file and returns a full name of the class The full name of the returned class is instantiated through reflection to form a concrete factory instance, and the factory instance generates the beans that the component specifically needs.

Earlier we mentioned the EnableAutoConfiguration annotation, and its class diagram is as follows:
Insert picture description here
you can find that it finally implements ImportSelector (selector) and BeanClassLoaderAware (bean class loader middleware), focusing on the selectImports method of AutoConfigurationImportSelector.
Insert picture description here
This method is executed before the springboot startup process-bean is instantiated, 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 relied on the components we need through the spring-boot starter dependency method, then the class information of these components is in the select method The middle can also be obtained, don't worry, we continue to analyze downward.

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

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

Insert picture description here

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

Insert picture description here
I found that Spring's @Configuration is like a springBean marked by annotations. Continue to look down:

  • @ConditionalOnClass({ SqlSessionFactory.class,
    SqlSessionFactoryBean.class}): The
    MybatisAutoConfiguration configuration class is parsed when there are two classes of SqlSessionFactory.class and SqlSessionFactoryBean.class, otherwise this configuration class will not be parsed, make sence, we need mybatis for us To return the session object, there must be a session factory related class.
  • @CondtionalOnBean(DataSource.class): Only process dataSource that has been declared as bean.
  • @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 longer, and the space limit is not enough to paste)

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 Spring context environment, so the configuration method of the above components is configured through the mybatis.xml file when Spring starts. To an effect.

Through analysis, we can find that as long as there is SqlSessionFactory.class, SqlSessionFactoryBean.class in the classpath of a SpringBoot project, and the dataSourceBean has been registered in the container, the automated configuration can be triggered, which means that we only need to add mybatis to the maven project The required dependencies can trigger the automatic configuration, but if the native dependency of mybatis is introduced, the automatic configuration class of each integrated function must be modified, and the effect will not be obtained out of the box.

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:

Insert picture description here
Here are all the dependencies in the pom.xml file in the source code of mybatis-spring-boot-starter:

Insert picture description here
Because of the transitivity of maven dependency, we can rely on all the classes that need to be automatically configured as long as we rely on the starter to achieve out-of-the-box functionality. It also shows that Springboot simplifies the large amount of XML configuration and complex dependency management brought by the Spring framework, allowing developers to pay more attention to the development of business logic.

If you think it's okay, you can give a thumbs up. If there are any shortcomings, you are welcome to advise.

The job is not easy, the technology is just like that, CRUD salary is not high every day, hey! Life is so hard! Especially this year is more difficult, so if there is a small partner who needs to find a job or a small partner who needs to learn, I have compiled Java learning materials and this year’s Java interview questions, which can be provided to everyone for free. You can click to enter if you need it , and the code: cszq , you can also follow + private message me. Hope to help everyone!

Insert picture description here

Insert picture description here

Finally, I wish you all the best in your work!

Guess you like

Origin blog.csdn.net/m0_45270667/article/details/108752083