Analysis of SpringBoot's startup principle

Introduction

When we develop any SpringBoot project, we will use the startup class shown below:

@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

The @SpringBootApplication annotation is the core annotation of Spring Boot. It is actually a composite annotation:

@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, there are only three Annotations that are actually important:

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

That is @SpringBootApplication = (默认属性)@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 three each time, so it is convenient to write one @SpringBootApplication. Next, we will introduce these 3 Annotations.

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 the use of the configuration form based on JavaConfig. Therefore, after the startup class is marked with @Configuration, In fact, it is also a configuration class of the IoC container.

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

(1) The expression level is
based on XML configuration:

<?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) Registered 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) Expression of 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 dependent bean creation method 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 that 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

@ComponentScan this annotation is very important in Spring, it corresponds to the element 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 load these bean definitions 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, so I will interpret it at the end. Do you still remember the Annotation definitions beginning 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 bean definitions that meet the conditions of automatic configuration into the IoC container, nothing more!

@EnableAutoConfiguration will automatically configure the project according to the jar dependencies in the classpath. For example, if 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 SpringFactoriesLoaderthe support of an original tool class of the Spring framework: @EnableAutoConfiguration can intelligently automatically configure the function to be successful!
Insert picture description here

Automatic configuration behind the scenes hero: SpringFactoriesLoader detailed

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 SpringBoot's autoconfigure dependency package, which can illustrate the problem well.

Therefore, the magic knight of @EnableAutoConfiguration automatic configuration becomes: Search all META-INF/spring.factoriesconfiguration files from the classpath , and org.springframework.boot.autoconfigure.EnableutoConfigurationinstantiate the corresponding configuration items into the corresponding IoC in the form of JavaConfig marked with @Configuration through reflection (Java Refletion) The container configuration class is 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, 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 characteristic 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 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 the SpringFactoriesLoader are traversed first. 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 configuration of the PropertySource and Profile to be used).

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 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 entire 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, configuring 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 creating context environment module
  • The third part is the automatic configuration module, which serves as the core of springboot automatic configuration, 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

Every 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 this 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
    Insert picture description here
  • A SpringApplication instance is created in the run method. In the construction method, we can find that it calls an 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 method implements the following key steps:

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 eventually inherits the ConfigurableEnvironment. The class diagram
Insert picture description here
can be seen as follows , *Environment finally implements the PropertyResolver interface, which we usually get through the environment object When the value method corresponding to Key is specified in the configuration file, the getProperty method of the propertyResolver interface is called

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
mainly depends on the two directions of its inheritance:

LifeCycle: Life cycle class, which defines whether start starts, stop ends, and isRunning runs 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 (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, which is the Spring factory loader. This object provides the loadFactoryNames method. The input parameters are factoryClass and classLoader, which means that you need to pass in the factory class name and corresponding class load in the above figure. According to the specified classLoader, the method will load the specified file under the search path of the class adder, namely 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 the final As an implementation class, 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 gets these collections, it can be obtained through reflection The class objects and construction methods of these classes eventually generate instances.
Insert picture description here
The factory interface and its several implementation class interface names The
following figure helps us visually understand the automatic configuration process.
Insert picture description here
The relationship diagram of the key components of SpringBoot automatic configuration
includes the spring.factories file in the META-INF file of mybatis-spring-boot-starter, spring-boot-starter-web and other components. In the automatic configuration module,SpringFactoriesLoader collects the full names of the classes in the file and returns an array of full names of the classes. The full names of the returned classes are instantiated through reflection to form a specific factory instance. The factory instance generates the beans that the component specifically needs.

Before we mentioned EnableAutoConfiguration notes that the class diagram as follows:
Insert picture description here
you can find it and ultimately the ImportSelector (selector) and BeanClassLoaderAware (bean class loader middleware), focus on what AutoConfigurationImportSelectoris selectImports方法。
Insert picture description here
the method to start the process --bean instantiated before springboot Is executed, 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 is learned through the method annotations that it returns a list of the class names of the auto-configuration classes, the method calls the loadFactoryNames method, and
Insert picture description here
you can see that the auto-configurator will be based on the incoming factoryClass by looking at the code above. GetName() finds the corresponding key in all spring.factories files under the project system path, and loads the classes inside. Let's select the spring.factories file under mybatis-spring-boot-autoconfigure and
Insert picture description here
enter org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration, and mainly look at the class header:
Insert picture description here
found Spring @Configuration, as if it were annotated with annotations springBean, continue to look down,

@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class}) This annotation means: when there are two classes of SqlSessionFactory.class, SqlSessionFactoryBean.class, the MybatisAutoConfiguration configuration class is parsed, otherwise this configuration class is not parsed, make sence, We need mybatis to return the session object for us, and 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 of the above components is configured with the mybatis.xml file when Spring starts. To an effect. Through analysis, we can find thatAs long as there are SqlSessionFactory.class, SqlSessionFactoryBean.class under the classpath of a SpringBoot project, and the dataSourceBean has been registered in the container, automated configuration can be triggered, which means that we only need to add some dependencies required by mybatis to the maven project. Can trigger automatic configuration, But if the native dependency of mybatis is introduced, the automation configuration class must be modified for each integrated function, and the effect of out-of-the-box use will not be obtained. So Spring-boot provides us with a unified starter that can directly configure related classes, and the dependencies required to trigger automatic configuration (mybatis) are as follows:
Insert picture description here
here is the intercepted source code of mybatis-spring-boot-starter in the pom.xml file All dependencies:
Insert picture description here
Because of the transitive nature of maven dependencies, 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 functions. 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.

This article is transferred from: https://www.cnblogs.com/xiaoxi/p/7999885.html

Guess you like

Origin blog.csdn.net/weixin_43246215/article/details/108502355