So what is the magic of Spring Boot? Automatic Configuration, Starting Dependency, Actuator, Command Line Interface (CLI) are the four most important Spring Boot Core features, among which CLI is an optional feature of Spring Boot. Although it is powerful, it also introduces a set of unconventional development models, so this series of articles only focuses on the other three features.
As the title of the article, this article is the first part of this series, which will open the door to Spring Boot for you, focusing on analyzing its startup process and automatic configuration implementation principles for you. To master this part of the core content and understand some basic knowledge of the Spring framework, you will get twice the result with half the effort.
1. Introducing bricks and jade: Exploring Spring IoC containers
If you have seen the source code of the SpringApplication.run()
method, Spring Boot’s extremely lengthy startup process will definitely drive you crazy. Looking at the essence through the phenomenon, SpringApplication just converts a typical Spring The startup process of the application has been expanded, so a thorough understanding of the Spring container is a key to open the door of Spring Boot.
1.1, Spring IoC container
You can compare the Spring IoC container to a restaurant. When you come to the restaurant, you usually greet the waiter directly: order! As for the ingredients of the dish? How to use raw materials to make dishes? Maybe you don’t care at all. The same is true for the IoC container. You only need to tell it that it needs a certain bean, and it will throw the corresponding instance (instance) to you. As for whether the bean depends on other components and how to complete its initialization, you don’t need to care at all.
As a restaurant, if you want to make dishes, you must know the ingredients and recipes of the dishes. Similarly, if the IoC container wants to manage various business objects and their dependencies, it needs to record and manage these information in some way . The BeanDefinition
object assumes this responsibility: each bean in the container will have a corresponding BeanDefinition instance, which is responsible for saving all the necessary information of the bean object, including the class type of the bean object, whether it is abstract or not Classes, constructors and parameters, other attributes, and so on. When the client requests the corresponding object from the container, the container will use this information to return a complete and available bean instance to the client.
The raw materials are ready (look at the raw materials in BeanDefinition), let’s start cooking, wait, you still need a recipe, BeanDefinitionRegistry
and BeanFactory
are this Recipe, BeanDefinitionRegistry abstracts the bean registration logic, and BeanFactory abstracts the bean management logic, and each BeanFactory implementation class undertakes the bean registration and management work.
The relationship between them is as follows:
DefaultListableBeanFactory
as a more general BeanFactory implementation, it also implements the BeanDefinitionRegistry interface, so it undertakes the Bean registration management work. It can also be seen from the figure that the BeanFactory interface mainly includes methods for managing beans such as getBean, containBean, getType, and getAliases, while the BeanDefinitionRegistry interface includes methods for registering and managing BeanDefinition such as registerBeanDefinition, removeBeanDefinition, and getBeanDefinition.
The following is a simple code to simulate how the underlying BeanFactory works:
// Default container implementation
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
// Construct the corresponding BeanDefinition according to the business object
AbstractBeanDefinition definition = new RootBeanDefinition(Business.class,true);
// Register the bean definition into the container
beanRegistry.registerBeanDefinition("beanName", definition);
// If there are multiple beans, you can also specify dependencies between each bean
//........
// Then you can get an instance of this bean from the container
// Note: The beanRegistry here actually implements the BeanFactory interface, so it can be forced to switch,
// Simple BeanDefinitionRegistry cannot be cast to BeanFactory type
BeanFactory container = (BeanFactory) beanRegistry;
Business business = (Business) container. getBean("beanName");
This code is only to illustrate the general workflow at the bottom of BeanFactory. The actual situation will be more complicated. For example, the dependencies between beans may be defined in external configuration files (XML/Properties) or in annotations. The entire workflow of the Spring IoC container can be roughly divided into two stages:
①, container startup phase
When the container starts, it will load ConfigurationMetaData
by some means. In addition to the relatively straightforward code method, in most cases, the container needs to rely on certain tool classes, such as: BeanDefinitionReader
, BeanDefinitionReader will parse and analyze the loaded ConfigurationMetaData
, And assemble the analyzed information into the corresponding BeanDefinition, and finally register these BeanDefinitions that save the bean definition to the corresponding BeaProfile (profile) and properties (properties), students with rich development experience must be familiar with these two things: different environments (eg: production environment, pre-release environment) can use different configuration files, and properties can be Obtained from sources such as configuration files, environment variables, command line parameters, etc. Therefore, when the Environment is ready, resources can be obtained from the Environment at any time throughout the application.
To sum up, the two lines of code at ② mainly accomplish the following things:
-
Determine whether the Environment exists, create it if it does not exist (if it is a web project, create
StandardServletEnvironment
, otherwise createStandardEnvironment
) -
Configure Environment: configure profile and properties
-
Call the
environmentPrepared()
method of SpringApplicationRunListener to notify the event listener: the application’s Environment is ready
③. The SpringBoot application will output something like this when it starts:
. ____ _ _ _ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
==========|_|===============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
If you want to change this thing into your own graffiti, you can study the implementation of the following Banner, this task is left to you.
④. Create different ApplicationContext containers according to whether it is a web project or not.
⑤. Create a series of FailureAnalyzer
. The creation process is still to get all the classes that implement the FailureAnalyzer interface through SpringFactoriesLoader, and then create the corresponding instances. FailureAnalyzer is used to analyze failures and provide relevant diagnostic information.
⑥. Initialize the ApplicationContext, which mainly completes the following tasks:
-
Set the prepared Environment to ApplicationContext
-
Traverse and call the
initialize()
method of all ApplicationContextInitializers to further process the created ApplicationContext -
Call the
contextPrepared()
method of SpringApplicationRunListener to notify all listeners that the ApplicationContext has been prepared -
Load all beans into the container
-
Call the
contextLoaded()
method of SpringApplicationRunListener to notify all listeners: ApplicationContext has been loaded
⑦. Call the refresh()
method of ApplicationContext to complete the last process available for the IoC container. From the name, it is understood as refreshing the container, so what is refreshing? It is to intervene in the startup of the container, and contact the content of the first section. How to refresh it? And look at the following code:
// Extracted from a line of code in the refresh() method
invokeBeanFactoryPostProcessors(beanFactory);
Look at the implementation of this method:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
…
}
Get all BeanFactoryPostProcessor
to do some extra operations on the container. BeanFactoryPostProcessor allows us to do some additional operations on the information saved by the BeanDefinition registered to the container before the container instantiates the corresponding object. The getBeanFactoryPostProcessors() method here can get 3 Processors:
ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
There are not so many BeanFactoryPostProcessor implementation classes, why are there only these three? Because among the various ApplicationContextInitializers and ApplicationListeners obtained in the initialization process, only the above three have done operations similar to the following:
public void initialize(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
Then you can enter the PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
method. In addition to traversing the above three BeanFactoryPostProcessor processes, this method will also obtain the type BeanDefinitionRegistryPostProcessor
The bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
, the corresponding Class is ConfigurationClassPostProcessor
. ConfigurationClassPostProcessor
is used to parse and process various annotations, including: @Configuration, @ComponentScan, @Import, @PropertySource, @ImportResource, @Bean. When processing the @import
annotation, EnableAutoConfigurationImportSelector.selectImports()
in the section will be called to complete the autoconfiguration function. I won’t talk about the others here, if you are interested, you can refer to Reference 6.
⑧. Find out whether CommandLineRunner and ApplicationRunner are registered in the current context, and if so, traverse and execute them.
⑨. Execute the finished() method of all SpringApplicationRunListener.
This is the entire startup process of Spring Boot. Its core is to add various extension points based on the initialization and startup of the Spring container. These extension points include: ApplicationContextInitializer, ApplicationListener, and various BeanFactoryPostProcessors, etc. You don’t have to pay too much attention to the details of the entire process, and it doesn’t even matter if you don’t understand it. You just need to understand when and how these extension points work, and make them work for you.
The entire startup process is indeed very complicated. You can query some chapters and contents in the reference materials, compare the source code, and read more. I think you can figure it out in the end. All in all, Spring is the core. If you understand the startup process of the Spring container, then the Spring Boot startup process is no problem.
Thank you for the summary, mark a wave.
Big brother original address: http://blog.51cto.com/luecsc/1964056
The bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
, the corresponding Class is ConfigurationClassPostProcessor
. ConfigurationClassPostProcessor
is used to parse and process various annotations, including: @Configuration, @ComponentScan, @Import, @PropertySource, @ImportResource, @Bean. When processing the @import
annotation, EnableAutoConfigurationImportSelector.selectImports()
in the section will be called to complete the autoconfiguration function. I won’t talk about the others here, if you are interested, you can refer to Reference 6.
⑧. Find out whether CommandLineRunner and ApplicationRunner are registered in the current context, and if so, traverse and execute them.
⑨. Execute the finished() method of all SpringApplicationRunListener.
This is the entire startup process of Spring Boot. Its core is to add various extension points based on the initialization and startup of the Spring container. These extension points include: ApplicationContextInitializer, ApplicationListener, and various BeanFactoryPostProcessors, etc. You don’t have to pay too much attention to the details of the entire process, and it doesn’t even matter if you don’t understand it. You just need to understand when and how these extension points work, and make them work for you.
The entire startup process is indeed very complicated. You can query some chapters and contents in the reference materials, compare the source code, and read more. I think you can figure it out in the end. All in all, Spring is the core. If you understand the startup process of the Spring container, then the Spring Boot startup process is no problem.
Thank you for the summary, mark a wave.
Big brother original address: http://blog.51cto.com/luecsc/1964056