If you don't understand the principle of Spring Boot startup, you must check it out!

When we develop any 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);
    }
}

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 need 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:

@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 annotate the original information, in fact only three Annotations are important:

  • @Configuration( @SpringBootConfigurationClick to view and find that it is still applied @Configuration)

  • @EnableAutoConfiguration

  • @ComponentScan

That is @SpringBootApplication= (default attribute) @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 @SpringBootApplicationit is convenient to write one. Next, the three Annotations are introduced separately.

1、@Configuration

This is @Configurationnot unfamiliar to us. It is the one used by the configuration class of the Spring Ioc container in the form of JavaConfig @Configuration. The SpringBoot community recommends using the configuration form based on JavaConfig. Therefore, after the startup class here is marked, @Configurationit is actually an IoC container. configuration class.

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

(1) Expression level

The XML-based configuration is as follows:

<?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 as follows:

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

Any annotated @ConfigurationJava class definition is a JavaConfig configuration class.

(2) Registration bean definition level

The XML-based configuration looks like this:

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

The configuration form based on JavaConfig is as follows:

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

For any marked @Beanmethod, 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 relationship 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 form based on JavaConfig is as follows:

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

@Configuration: When you mention it, @Configurationyou have 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>

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

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

@BeanThe 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

@ComponentScanThis annotation is very important in Spring. It corresponds to the elements in the XML configuration. @ComponentScanIts function is to automatically scan and load qualified components (such as @Componentand @Repositoryetc.) or bean definitions, and finally load these bean definitions into the IoC container.

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

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

3、@EnableAutoConfiguration

I personally feel that @EnableAutoConfigurationthis Annotation is the most important, so I will interpret it at the end. Do you still remember the @EnableAnnotation definition with various names provided by the Spring framework? For @EnableScheduling、@EnableCaching、@EnableMBeanExportexample, @EnableAutoConfigurationthe concept and way of doing things are actually in the same line. A brief summary is that, with the help @Importof support, collect and register bean definitions related to specific scenarios.

@EnableSchedulingIt is to load all bean definitions related to the Spring scheduling framework into the IoC container through @Import.
@EnableMBeanExportIt is to load JMX-related bean definitions into the IoC container through @Import.
But @EnableAutoConfigurationwith the help of @Import, all bean definitions that meet the automatic configuration conditions are loaded into the IoC container, that's all!

@EnableAutoConfigurationThe project will be automatically configured according to the jar dependencies in the class path. For example, if dependencies are added spring-boot-starter-web, the dependencies of Tomcat and Spring MVC will be automatically added, and Spring Boot will automatically configure Tomcat and Spring MVC.

As a composite Annotation, @EnableAutoConfiguration defines 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 important thing is to use the IoC container @Import(EnableAutoConfigurationImportSelector.class)that EnableAutoConfigurationImportSelector,@EnableAutoConfigurationcan help the SpringBoot application to load all eligible @Configurationconfigurations into the current SpringBoot creation and use. SpringFactoriesLoaderJust like an "octopus", with the support of an original tool class of the Spring framework: the @EnableAutoConfigurationintelligent automatic configuration function can be accomplished!

Automatically configure the behind-the-scenes heroes: SpringFactoriesLoader in detail

SpringFactoriesLoader is a private extension scheme of the Spring framework, and 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) {
        ....
    }
}

If it is used together @EnableAutoConfiguration, it provides more functional support for configuration search, that is, according to the @EnableAutoConfigurationcomplete class name of the class org.springframework.boot.autoconfigure.EnableAutoConfigurationas the search key, a corresponding set of @Configurationclasses can be obtained.

image

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

Therefore, @EnableAutoConfigurationthe magic knight of 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 @ConfigurationIoC container configuration class in the form of JavaConfig marked through reflection (Java Refletion), It is then aggregated into one and loaded into the IoC container.


2. In-depth exploration of SpringApplication execution process

The implementation of the run method of SpringApplication 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 instance method of the created SpringApplication. When the SpringApplication instance is initialized, it will do several things in advance:

  • According to whether there is a characteristic class in the classpath, org.springframework.web.context.ConfigurableWebApplicationContextit is determined whether an ApplicationContext type for Web applications should be created.

  • Use SpringFactoriesLoaderfinds and loads all available ones in the application's classpath ApplicationContextInitializer.

  • Use SpringFactoriesLoaderfinds and loads all available ones in the application's classpath ApplicationListener.

  • Infer and set the defining 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 starts to be executed. At the beginning of the method execution, it first traverses and executes all the methods that can be found SpringFactoriesLoaderand loaded SpringApplicationRunListener. Call their started()methods to tell them SpringApplicationRunListener, "Hey, the SpringBoot application is about to start executing!".

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

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

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

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

7) After the ApplicationContext is created, SpringApplication will use it again Spring-FactoriesLoaderto find and load all the available ones in the classpath ApplicationContext-Initializer, and then traverse and call these ApplicationContextInitializer( applicationContext) methods to further process initializethe created ones .ApplicationContext

8) Traverse and call all SpringApplicationRunListenermethods contextPrepared().

9) The core step is to @EnableAutoConfigurationload all the configurations obtained before and other forms of IoC container configurations to the prepared one ApplicationContext.

10) Traverse and call all SpringApplicationRunListenermethods contextLoaded().

11) The method called ApplicationContextto refresh()complete the last process available to the IoC container.

12) Find out ApplicationContextwhether there are registrations in the current system CommandLineRunner, and if so, traverse and execute them.

13) Under normal circumstances, traverse the execution SpringApplicationRunListenermethod finished(), (if an exception occurs in the whole process, all methods will still be called SpringApplicationRunListener, finished()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 analyzes its startup logic and automatic configuration principles with reference to the main class diagram in the process.

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 to initialize the module of SpringApplication and configure some basic environment variables, resources, constructors and listeners;

  • The second part implements the specific startup scheme of the application, including the monitoring module of the startup process, the loading configuration environment module, and the core creation context module;

  • The third part is the automatic configuration module, which is the core of springboot automatic configuration and will be discussed in detail in the following analysis. In the startup routine below we will wire up the main functions in the structure.

start up:

Each SpringBoot program has a main entry, which is the main method, which calls and starts SpringApplication.run()the entire spring-boot program. The class where the method is located needs to use @SpringBootApplicationannotations, and @ImportResourceannotations (if need), @SpringBootApplicationincluding three annotations, the functions are as follows:

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

  • @SpringBootConfiguration(Internal @Configuration): The marked class is equal to ( applicationContext.xml) in the spring XML configuration file, assembling all bean transactions, and providing a spring context environment.

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

image

SpringBoot startup class

First enter the run method

image.gif

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

This 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. Create an application listener SpringApplicationRunListenersand start listening

2. Load the SpringBoot configuration environment ( ConfigurableEnvironment), if it is published through the web container, it will be loaded StandardEnvironment, and it will eventually be inherited ConfigurableEnvironment. The class diagram is as follows

image

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. 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 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 (by judging whether it is web environment), select the annotation context by default AnnotationConfigApplicationContext(load beans by scanning all annotation classes), and finally instantiate through BeanUtils the context object and return it.

The ConfigurableApplicationContext class diagram is as follows:

It mainly depends on the two directions of its inheritance:

  • LifeCycle: life cycle class, which defines start start, stop end, isRunning whether to run 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 listeners、environment、applicationArguments、bannerassociates other important components with the context object

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

After the configuration, Springboot did some basic finishing work and returned the application environment context. Looking back at the overall process, the startup of Springboot mainly creates the configuration environment (environment), event listeners (listeners), and application context (applicationContext), and based on the above conditions, we start instantiating the beans we need in the container. So far, through SpringBoot startup The program has been constructed. Next, 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 invoked the SpringBoot automatic configuration module.

SpringBoot automatic configuration module

The main use of this configuration module SpringFactoriesLoaderis the Spring factory loader. This object provides loadFactoryNamesmethods. The input parameters are factoryClass and classLoader, that is, the name of the factory class in the above figure and the corresponding class loader need to be passed in. The method will be based on the specified classLoader, load the specified file under the search path of the class adder, that is, spring.factoriesthe 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 as follows This kind of one-to-many class name collection, after obtaining the class names of these implementation classes, loadFactoryNamesthe method returns the class name collection, and the method caller obtains these collections, and then obtains the class objects and construction methods of these classes through reflection, and finally generates instance.

Factory interface and several implementation class interface names

The figure below helps us visualize the automatic configuration process.

SpringBoot automatic configuration key component relationship diagram

mybatis-spring-boot-starter, spring-boot-starter-weband other components’ META-INF files contain spring.factoriesfiles. In the automatic configuration module, SpringFactoriesLoaderthe full name of the class in the file is collected and an array of the full name of the class is returned. The returned full name of the class is instantiated through reflection, forming a specific The factory instance, the factory instance to generate the beans that the component specifically needs.

We mentioned EnableAutoConfigurationannotations before, and 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 following AutoConfigurationImportSelectormethods selectImports.

[External link image transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the image and upload it directly (img-Dm6tiUWw-1597051375071) (https://upload-images.jianshu.io/upload_images/18688925-97932faefd1184cf?imageMogr2 /auto-orient/strip%7CimageView2/2/w/1240)]

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 that we have relied on the components we need through the spring-boot starter dependency, the class information of these components is in the select method. It can also be obtained, don't worry, let's continue to analyze down.

The method in this method getCandidateConfigurations, learned through the method annotation, returns a list of class names of the automatic configuration class, the method calls the loadFactoryNamesmethod, view the method

image.gif

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

image.gif

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

Found that Spring @Configurationis like a springBean marked with annotations, continue to look down,

  • @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class}): When these two classes exist SqlSessionFactory.class, the configuration class will be parsed, otherwise this configuration class will not be parsed, make sense, we need mybatis to return the session object for us, and there must be session factory related classes.SqlSessionFactoryBean.classMybatisAutoConfiguration

  • @CondtionalOnBean(DataSource.class): Only deal with 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, the 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 sqlSessionFactory、sqlSessionTemplate、dataSourcethat the components required by mybatis can be automatically configured. @ConfigurationThe annotation has provided the Spring context, so the configuration method of the above components has the same effect as the configuration through the mybatis.xml file when Spring starts.

Through analysis, we can find that as long as a class path based on the SpringBoot project exists SqlSessionFactory.class, SqlSessionFactoryBean.classand the dataSourceBean has been registered in the container, automatic configuration can be triggered, which means that we only need to add several dependencies required by mybatis to the maven project. Automatic configuration can be triggered, but if the native dependency of mybatis is introduced, the automatic configuration class must be modified for each integrated function, so 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 mybatis-spring-boot-starterall dependencies in the pom.xml file in the intercepted source code:

Because of the transitive nature of maven dependencies, we can rely on all 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.

at last

Pay attention to the official account: Programmer Chasing the Wind. Reply 003 Get the latest 2020 Java interview question manual (more than 200 pages of PDF documents)

Guess you like

Origin blog.csdn.net/Design407/article/details/107917917