Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the startup process of Spring Boot and how to realize automatic configuration

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly explains the "Spring Boot startup process is what and how to achieve automatic configuration", the article explains the content is simple and clear, easy to learn and understand, the following please follow the editor's ideas slowly in-depth, together to study and learn "Spring Boot startup process is what and how to achieve automatic configuration" it!

Explore the Spring IOC Container

If you've seen the source code for the SpringApplication.run () method, Spring Boot's lengthy startup process is sure to drive you crazy.

Looking at the essence through the phenomenon, SpringApplication only extends the startup process of a typical Spring application, so a thorough understanding of the Spring container is the key to open the door of Spring Boot.

Spring IOC container

Spring IOC container can be compared to a restaurant, when you come to the restaurant, usually directly greet the waiter: order! As for the ingredients of the dish, what is it? How to cook a dish with raw materials? Maybe you don't care.

The same is true of the IOC container, you just need to tell it that it needs a certain bean, and it throws the corresponding instance to you. As to whether the bean depends on other components and how to initialize it, you don't need to care at all.

As a restaurant, if you want to make a dish, you need to know the ingredients and recipes of the dish. Similarly, if the IOC container wants to manage the various business objects and their dependencies, it needs to record and manage this information in some way.

The BeanDefinition object assumes this responsibility: each bean in the container has a corresponding BeanDefinition instance.

This instance is responsible for holding all the necessary information about the bean object, including the class type of the bean object, whether it is an abstract class, constructors and parameters, other properties, and so on.

When the client requests the corresponding object from the container, the container returns a fully available bean instance for the client through this information.

The raw materials are ready (think of BeanDefinition as the raw material), start cooking, and so on, you also need a recipe.

BeanDefinitionRegistry and BeanFactory are the recipes, and BeanDefinitionRegistry abstracts the registration logic of bean.

BeanFactory abstracts the management logic of bean, and the implementation classes of each BeanFactory undertake the registration and management of bean.

The relationship between them is shown in the following figure:

BeanFactory and BeanDefinitionRegistry diagrams (from: Spring)

As a general BeanFactory implementation, DefaultListableBeanFactory also implements the BeanDefinitionRegistry interface, so it undertakes the registration management of bean.

As can be seen from the figure, the BeanFactory interface mainly contains getBean, containBean, getType, getAliases and other methods to manage bean.

The BeanDefinitionRegistry interface contains registerBeanDefinition, removeBeanDefinition, getBeanDefinition and other methods to register and manage BeanDefinition.

Here is a simple piece of code to simulate how the underlying BeanFactory works:

This code is only intended to illustrate the general workflow at the bottom of BeanFactory, the actual situation will be more complicated.

For example, dependencies between bean may be defined in an external configuration file (XML/Properties) or annotated.

The entire workflow of the Spring IoC container can be roughly divided into two phases:

① container startup phase

When the container starts, the ConfigurationMetaData is loaded in some way. In addition to being more straightforward in the code, in most cases, the container needs to rely on certain utility classes.

For example: BeanDefinitionReader, which parses and analyzes the loaded ConfigurationMetaData, and assembles the analyzed information into the corresponding BeanDefinition.

* save the BeanDefinition defined by bean and register with the corresponding BeanDefinitionRegistry, so that the startup of the container is completed.

This phase mainly completes some preparatory work, with more emphasis on the collection of bean object management information, of course, some confirmatory or auxiliary work is also completed at this stage.

Let's look at a simple example. In the past, all bean were defined in the XML configuration file. The following code simulates how BeanFactory loads bean definitions and dependencies from the configuration file:

Instantiation phase of ② Bean

After the * * phase, all bean definitions are registered to the BeanDefinitionRegistry through BeanDefinition.

When a request requests an object through the container's getBean method, or because the dependency container needs to implicitly call getBean, the second phase of activity is triggered: the container first checks to see if the requested object has been instantiated before.

If not, the requested object is instantiated and dependencies are injected into it based on the information provided by the registered BeanDefinition. When the object is assembled, the container immediately returns it to the requester for use.

BeanFactory is just an implementation of the Spring IoC container, and if not specified, it uses a deferred initialization strategy: initialization and dependency injection are performed on an object in the container only when that object is accessed.

In the actual scenario, we use more of another type of container: ApplicationContext, which is built on top of BeanFactory and belongs to a more advanced container.

In addition to all the capabilities of BeanFactory, it also provides support for event listening mechanism and internationalization. It manages the bean and completes all initialization and dependency injection operations when the container starts.

Spring Container extension Mechanism

The IoC container is responsible for managing the lifecycle of all bean in the container, and at different stages of the bean lifecycle, Spring provides different extension points to change the fate of bean.

In the startup phase of 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, such as modifying some properties defined by bean or adding other information.

If you want to customize the extension class, you usually need to implement the org.springframework.beans.factory.config.BeanFactoryPostProcessor interface.

At the same time, because there may be multiple BeanFactoryPostProcessor in the container, you may also need to implement the org.springframework.core.Ordered interface to ensure that the BeanFactoryPostProcessor is executed sequentially.

Spring provides a small number of BeanFactoryPostProcessor implementations, and we use PropertyPlaceholderConfigurer to illustrate its general workflow.

In the XML configuration file of a Spring project, you can often see that the values of many configuration items use placeholders and configure the values represented by placeholders to separate properties files.

In this way, the configurations scattered in different XML files can be managed centrally, and it is also convenient for operators to configure different values according to different environments. This very useful function is implemented by PropertyPlaceholderConfigurer.

According to the previous article, when BeanFactory loads all the configuration information during the * * phase, the properties of the objects saved in BeanFactory still exist as placeholders, such as ${jdbc.mysql.url}.

When PropertyPlaceholderConfigurer is applied as a BeanFactoryPostProcessor, it replaces the attribute value represented by the placeholder in the corresponding BeanDefinition with the value in the properties configuration file.

When we need to instantiate bean, the property value in the bean definition has been replaced with the value we configured.

Of course, its implementation is a little more complex than described above, here only describes its general working principle, a more detailed implementation can refer to its source code.

Similarly, there is BeanPostProcessor, which exists during the object instantiation phase. Similar to BeanFactoryPostProcessor, it handles all eligible and instantiated objects in the container.

In simple contrast, BeanFactoryPostProcessor handles the definition of bean, while BeanPostProcessor handles objects after bean has been instantiated.

BeanPostProcessor defines two interfaces:

To understand the timing of these two methods, simply take a look at the entire lifecycle of bean:

The instantiation process of Bean (from: Spring revelation)

The postProcessBeforeInitialization () method and postProcessAfterInitialization () correspond to the methods that will be performed by the pre-processing and post-processing steps in the figure, respectively.

References to bean object instances are passed in both methods, which facilitates the object instantiation process of the extension container, where almost any operation can be performed on the incoming instance.

Annotations, AOP and other functions are widely used in the implementation of BeanPostProcessor, for example, there is a custom annotation, you can fully implement the interface of BeanPostProcessor, in which you can determine whether the annotation is on the head of the bean object.

If so, you can do anything with this bean instance, isn't it very simple?

Looking at a more common example, you can often see a variety of Aware interfaces in Spring, whose purpose is to inject the dependencies specified in the Aware interface definition into the current instance after the object instantiation is complete.

For example, the most common ApplicationContextAware interface, a class that implements this interface can get an ApplicationContext object.

When the instantiation process of each object in the container reaches the stage of BeanPostProcessor pre-processing, the container detects the ApplicationContextAwareProcessor that was previously registered with the container.

It then calls its postProcessBeforeInitialization () method to check and set the Aware-related dependencies.

Take a look at the code, isn't it simple:

To sum up, this section reviews some of the core contents of the Spring container with you. You can't write more because of the limited space, but understanding this part is enough to make it easy for you to understand the startup principle of Spring Boot.

If you encounter some obscure knowledge in the follow-up learning process, it may have an unexpected effect to look back at the core knowledge of Spring.

Maybe there are few Chinese materials in Spring Boot, but there are too many Chinese materials and books in Spring. There is always something that can enlighten you.

Tamping the Foundation: JavaConfig and Common Annotation

JavaConfig

We know that bean is a very core concept in Spring IOC, and the Spring container is responsible for the lifecycle management of bean.

At first, Spring used XML configuration files to describe the definition of bean and their dependencies, but with the development of Spring, more and more people expressed dissatisfaction with this approach.

Because all the business classes of the Spring project are configured in the XML file in the form of bean, resulting in a large number of XML files, the project becomes complex and difficult to manage.

Later, Guice, a framework based on pure Java Annotation dependency injection, came out, and its performance is obviously better than that of Spring based on XML.

Some people even think that Guice can completely replace Spring (Guice is only a lightweight IOC framework, it is still a long way from replacing Spring).

It is this sense of crisis that prompted Spring and the community to launch and continuously refine the JavaConfig subproject, which describes the dependency binding relationship between bean based on Java code and Annotation annotations.

For example, the following is a definition that uses XML configuration to describe bean:

The JavaConfig-based configuration looks like this:

If there is a dependency between two bean, it should look like this in the XML configuration:

In JavaConfig, it is like this:

You may notice that in this example, two bean depend on dependencyService, that is, dependencyService () is called when initializing bookService, and dependencyService () is called when otherService is initialized, so here's the problem?

Is there one dependencyService instance or two in the IOC container at this time? Let's leave this question for everyone to think about. I won't repeat it here.

@ ComponentScan

The @ ComponentScan annotation corresponds to the element in the XML configuration form, which means that component scanning is enabled, and Spring automatically scans all bean configured through the annotation and registers it with the IOC container.

We can specify the scope of @ ComponentScan automatic scanning through attributes such as basePackages, and if not, it will be scanned by default from the package of the class where @ ComponentScan is declared. Because of this, the startup classes of SpringBoot are all under src/main/java by default.

@ Import

The @ Import annotation is used to import configuration classes, for a simple example:

Now that there is another configuration class, such as MoonUserConfiguration, there is a bean in this configuration class that depends on the bookService in MoonBookConfiguration. How do you combine the two bean?

With the help of @ Import:

It is important to note that before 4.2, the @ Import annotation only supported importing configuration classes, but after 4.2, it supported importing normal classes and registering this class with the IOC container as a definition of bean.

@ Conditional

The @ Conditional annotation indicates that a bean is initialized or some configuration is enabled only after certain conditions have been met.

It is commonly used on classes identified by @ Component, @ Service, @ Configuration, and so on, or on methods marked by @ Bean.

If an @ Configuration class is marked @ Conditional, all methods in the class that identify @ Bean and related classes imported by the @ Import annotation will comply with these conditions.

You can easily write your own conditional class in Spring. All you have to do is implement the Condition interface and override its matches () method.

For example, the following simple conditional class means that it takes effect only if the JdbcTemplate class exists in Classpath:

When you use Java to declare bean, you can use this custom condition class:

In this example, the MyService bean is created only if the condition of the JdbcTemplateCondition class is true.

In other words, the creation condition of the MyService bean is that the classpath contains JdbcTemplate, otherwise the declaration of the bean will be ignored.

Spring Boot defines a number of interesting conditions and applies them to configuration classes that form the basis for automatic configuration of Spring Boot.

Spring Boot uses conditional configuration by defining several special conditional annotations and applying them to the configuration class.

Some of the conditional comments provided by Spring Boot are listed below:

@ ConfigurationProperties and @ EnableConfigurationProperties

When the values of certain properties need to be configured, we usually create a new configuration item in the application.properties file, and then use the @ Value annotation in bean to get the configuration value.

For example, the following code is used to configure the data source:

Attributes injected with @ Value annotations are usually relatively simple, and it is not easy to maintain if the same configuration is used in multiple places (consider, what should you do if dozens of places are using a configuration and now you want to change the name?)

For more complex configurations, Spring Boot provides a more elegant implementation, which is the @ ConfigurationProperties annotation.

We can rewrite the above code in the following ways:

@ ConfigurationProperties is also handy for more complex configurations, such as the following configuration files:

You can define the following configuration classes to receive these properties:

The @ EnableConfigurationProperties annotation indicates the embedded support for @ ConfigurationProperties. By default, the corresponding Properties Class will be injected into the IOC container as bean, that is, the @ Component annotation is not required on the corresponding Properties class.

Cutting iron like mud: detailed explanation of SpringFactoriesLoader

JVM provides three kinds of loaders: BootstrapClassLoader, ExtClassLoader and AppClassLoader load the Java core class library, the extended class library and the class library under the application's class path (CLASSPATH), respectively.

JVM loads the class through the parent delegation model, and we can also implement our own class loader by inheriting from java.lang.classloader.

What is the parental delegation model? When a class loader receives a class loading task, it will first give it to its own parent loader to complete.

So eventually the load task is passed to the top-level BootstrapClassLoader, and only when the parent loader cannot complete the load task will it try to load itself.

One advantage of using the parent delegation model is to ensure that the end result of using different class loaders is the same object, which ensures the type safety of the Java core library.

For example, loading the java.lang.Object class in the rt.jar package, no matter which loader loads the class, is ultimately delegated to the top-level BootstrapClassLoader to load, which ensures that any class loader will end up with the same Object object.

Look at the source code of ClassLoader to get a more intuitive understanding of the parent delegation model:

However, the parent delegation model does not solve all classloader problems. For example, Java provides a number of service provider interfaces (ServiceProviderInterface,SPI) that allow third parties to provide implementations for these interfaces.

The common SPI includes JDBC, JNDI, JAXP and so on. The interfaces of these SPI are provided by the core class library but implemented by the third party.

There is a problem: the interface to SPI is part of the Java core library and is loaded by BootstrapClassLoader; Java classes implemented by SPI are typically loaded by AppClassLoader.

BootstrapClassLoader cannot find the implementation class of SPI because it only loads the core library of Java.

It also cannot be proxied to AppClassLoader because it is the top-most classloader. In other words, the parental delegation model does not solve this problem.

The thread context class loader (ContextClassLoader) just solves this problem. In terms of name, it may be misunderstood that it is a new class loader, but in fact, it is just a variable of the Thread class.

You can set and get the object through setContextClassLoader (ClassLoadercl) and getContextClassLoader ().

If nothing is set, the context class loader for threads of Java applications defaults to AppClassLoader.

When the core class library uses the SPI interface, the passed class loader can be successfully loaded into the class implemented by SPI using the thread context class loader.

Thread context class loaders are used in many SPI implementations. But in JDBC, you may see a more straightforward implementation.

For example, in the loadInitialDrivers () method in the JDBC driver management java.sql.Driver, you can directly see how JDK loads the driver:

In fact, the most important thing to explain the thread context class loader is not to look confused when you see Thread.currentThread (). GetClassLoader () and Thread.currentThread (). GetContextClassLoader ().

Except that the ClassLoader obtained in many underlying frameworks may be different, it is the same in most other business scenarios, as long as you know what problem it is to solve.

In addition to loading class, the classloader also has a very important function, which is to load resources, which can read any resource file from the jar package.

For example, the ClassLoader.getResources (Stringname) method is used to read the resource file in the jar package with the following code:

Do you feel a little familiar? yes, its logic is actually the same as the logic of class loading. First, determine whether the parent class loader is empty or not, then entrust the parent class loader to perform the resource lookup task until it is BootstrapClassLoader,*** 's turn to find it.

Different class loaders are responsible for scanning jar packages in different paths, just like loading class. * scans all jar packages to find qualified resource files.

The findResources (name) method of the classloader iterates through all the jar packages it is responsible for loading and finds the resource file named name in the jar package, where the resource can be any file, or even a .class file.

For example, the following example is used to find the Array.class file:

After running, you can get the following results:

According to the URL of the resource file, the corresponding file can be constructed to read the resource content.

You may find it strange to see here. Don't you want to explain SpringFactoriesLoader in detail? What does it mean to talk about a bunch of ClassLoader?

Just take a look at its source code and you'll see:

With the previous knowledge about ClassLoader, and then to understand this code, is it suddenly enlightened: search for all META-INF/spring.factories configuration files in each jar package under CLASSPATH, and then parse the properties file, find the configuration with the specified name and return.

It should be noted that, in fact, it will not only look under the ClassPath path, but also scan the jar package under all paths, but this file will only be found in the jar package under ClassPath.

Let's take a brief look at the contents of the spring.factories file:

After executing loadFactoryNames (EnableAutoConfiguration.class,classLoader), you get a corresponding set of @ Configuration classes.

We can instantiate these classes through reflection and then inject them into the IOC container. The * * container has a series of configuration classes in the form of JavaConfig labeled @ Configuration.

This is SpringFactoriesLoader, which is essentially a private extension of the Spring framework, similar to many of the core functions of SPI,Spring Boot based on Spring are based on this, I hope you can understand.

Another weapon: event listening Mechanism of Spring Container

In the past, event monitoring mechanisms were mostly used in graphical interface programming, such as clicking buttons and typing content in text boxes, which were called events.

When the event is triggered, the application makes a certain response that the application listens for the event, while on the server side, the event listening mechanism is more used for asynchronous notification, monitoring and exception handling.

Java provides two basic classes that implement the event listening mechanism: custom event types are extended from java.util.EventObject and event listeners are extended from java.util.EventListener.

Let's look at a simple example: simply monitoring the time-consuming of a method.

First define the event type, which is usually done by extending the EventObject, and the corresponding state is usually encapsulated in this class as the event occurs:

After the event is published, the corresponding listener can handle this type of event, and we can issue a begin event before the method starts execution.

Issue an end event after method execution, and accordingly, the event listener needs to provide a method to handle the events received in both cases:

The event listener API actually provides the corresponding handling method definition for different event publishing. The most important thing is that the method only receives the MethodMonitorEvent parameter, indicating that the listener class is only responsible for and handles the events corresponding to the listener.

With events and listeners, all that is left is to publish the event, and then let the appropriate listener listen and handle it.

Typically, we have an event publisher that acts as the event source and publishes the corresponding event to the corresponding event listener at the right time:

There are usually two things to pay attention to for event publishers (event sources):

Release events at the right time. The methodMonitor () method in this example is the source of the event publication, which issues MethodMonitorEvent events at two time points before and after the method execution, and the events published at each point in time are passed to the appropriate listener for processing.

In the specific implementation, it should be noted that the event release is executed sequentially, in order not to affect the processing performance, the processing logic of the event listener should be as simple as possible.

Management of event listeners. The publisher class provides methods for registering and removing event listeners, so that the client can decide whether to register a new listener or remove a listener according to the actual situation.

If the remove method is not provided here, the registered listener example will always be referenced by MethodMonitorEventPublisher, and even if it has been deprecated, it will still be in the publisher's listener list, which can lead to hidden memory leaks.

Event listening mechanism in Spring container

All event types in the ApplicationContext container of Spring inherit from org.springframework.context.AppliationEvent.

All listeners in the container implement the org.springframework.context.ApplicationListener interface and are registered in the container as bean.

Once events of the ApplicationEvent and its subtypes are published within the container, the ApplicationListener registered with the container handles these events.

You should have guessed what's going on.

ApplicationEvent inherits some default implementations from EventObject,Spring. For example, ContextClosedEvent represents the event type that the container publishes when it is about to close, and ContextRefreshedEvent represents the event type that the container publishes when it is initialized or refreshed.

The container internally uses ApplicationListener as the event listener interface definition, which inherits from EventListener.

When the ApplicationContext container starts, it automatically recognizes and loads the bean of type EventListener, and notifies the EventListener registered with the container once there are events published in the container.

The ApplicationContext interface inherits the ApplicationEventPublisher interface, which provides the voidpublishEvent (ApplicationEventevent) method definition. It is not difficult to see that the ApplicationContext container acts as the event publisher.

If you are interested, you can check the source code of the AbstractApplicationContext.publishEvent (ApplicationEventevent) method: ApplicationContext delegates the publishing of events and the management of listeners to the implementation class of the ApplicationEventMulticaster interface.

When the container starts, it checks to see if there is an instance of the ApplicationEventMulticaster object named applicationEventMulticaster in the container.

If so, use the implementation provided by it, without initializing a SimpleApplicationEventMulticaster as the implementation by default.

*. If our business needs to publish events inside the container, we only need to inject ApplicationEventPublisher dependencies into it to implement the ApplicationEventPublisherAware API or ApplicationContextAware API (please review the Aware API above).

Miraculous: revealing the principle of automatic configuration

The startup class of a typical Spring Boot application is generally located under the src/main/java root path, such as the MoonApplication class:

@ SpringBootApplication turns on component scanning and automatic configuration, while SpringApplication.run starts the boot application.

SpringBootApplication is a composite Annotation that combines three useful annotations:

@ SpringBootConfiguration is @ Configuration, which is an annotation of the Spring framework, indicating that the class is a JavaConfig configuration class.

While @ ComponentScan enables component scanning, which has been explained in detail earlier, the focus here is on @ EnableAutoConfiguration.

The @ EnableAutoConfiguration annotation means to enable Spring Boot auto-configuration. Spring Boot will guess the bean you need according to the application dependency, custom bean, whether there is a class under Classpath and other factors, and then register it in the IOC container.

So how does @ EnableAutoConfiguration calculate your needs? First of all, take a look at its definition:

Your focus should be on @ Import (EnableAutoConfigurationImportSelector.class).

As mentioned earlier, the @ Import annotation is used to import a class and register the class in the container as a definition of bean, where it will inject EnableAutoConfigurationImportSelector into the container as bean.

This class loads all eligible @ Configuration configurations into the container and takes a look at its code:

This class scans all Jar packages and injects all eligible @ Configuration configuration classes into the container. For what is eligible, see the contents of the META-INF/spring.factories file:

Take DataSourceAutoConfiguration as an example to see how Spring Boot is automatically configured:

Let's say separately:

ConditionalOnClass ({DataSource.class,EmbeddedDatabaseType.class}): enable this configuration only if there is a DataSource or EmbeddedDatabaseType class in the Classpath, otherwise it will be ignored.

@ EnableConfigurationProperties (DataSourceProperties.class): injects the default configuration class of DataSource into the IOC container. DataSourceproperties is defined as:

@ Import ({Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class}): import other additional configurations. Take DataSourcePoolMetadataProvidersConfiguration as an example:

DataSourcePoolMetadataProvidersConfiguration is a configuration class for database connection pool providers.

That is, if org.apache.tomcat.jdbc.pool.DataSource.class exists in Classpath, tomcat-jdbc connection pooling is used, and Hikari connection pooling is used if HikariDataSource.class is present in Classpath.

This describes only the tip of the iceberg of DataSourceAutoConfiguration, but it is sufficient to illustrate how Spring Boot uses conditional configuration to achieve automatic configuration.

Recall that the EnableAutoConfigurationImportSelector class was imported into @ EnableAutoConfiguration.

The selectImports () of this class gets a large number of configuration classes through SpringFactoriesLoader, and each configuration class makes decisions based on conditional configuration to achieve automatic configuration.

The whole process is clear, but one big question is left out: when was EnableAutoConfigurationImportSelector.selectImports () executed?

This method is actually executed during container startup: AbstractApplicationContext.refresh (), more details are described in the next section.

Boot Boot: the Secret of Spring Boot Application Startup

SpringApplication initialization

The whole startup process of Spring Boot is divided into two steps: initializing a SpringApplication object and executing the run method of the object.

Take a look at the initialization process of SpringApplication. The initialize (Object [] sources) method is called in the constructor of SpringApplication. The code is as follows:

The most important thing in the initialization process is to find the implementation class names of the ApplicationContextInitializer and ApplicationListener interfaces configured in the spring.factories file through SpringFactoriesLoader, so that the corresponding instances can be constructed later.

The main purpose of ApplicationContextInitializer is to further set up or process ConfigurableApplicationContext instances before ConfigurableApplicationContext does refresh.

ConfigurableApplicationContext inherits from ApplicationContext and mainly provides the ability to set up ApplicationContext.

Implementing an ApplicationContextInitializer is very simple because it has only one method, but in most cases there is no need to customize an ApplicationContextInitializer.

Even for the Spring Boot framework, it only registers two implementations by default. After all, the Spring container is so mature and stable that you don't have to change it.

There is nothing to say about the purpose of ApplicationListener. It is a framework implementation of Java event monitoring mechanism in Spring framework. The specific content is explained in detail in the previous section on Spring event monitoring mechanism.

The main point here is, if you want to add listeners to Spring Boot applications, how do you do that?

Spring Boot provides two ways to add custom listeners:

Add one or more custom listeners through SpringApplication.addListeners (ApplicationListener...listeners) or SpringApplication.setListeners (Collection > listeners).

Since the implementation class of SpringApplication has been obtained from spring.factories in the initialization process of ApplicationListener, we can directly add the configuration in the META-INF/spring.factories file of our jar package:

That's all we have to say about initialization of SpringApplication.

Spring Boot startup process

The entire startup process of the Spring Boot application is encapsulated in the SpringApplication.run method, and the whole process is really too long, but in essence, it has made a lot of extensions based on the startup of the Spring container. Take a look at the source code according to this idea:

① finds and loads all SpringApplicationRunListeners through SpringFactoriesLoader.

Notify all SpringApplicationRunListeners by calling the starting () method that the application is started.

SpringApplicationRunListeners is essentially an event publisher, which publishes different application event types (ApplicationEvent) at different points in time when SpringBoot applications are launched.

If there are any event listeners (ApplicationListener) who are interested in these events, they can be received and processed.

Remember that during the initialization process, SpringApplication loaded a series of ApplicationListener?

There is no code for publishing events in this startup process, but it has already been implemented here in SpringApplicationRunListeners.

To briefly analyze the implementation process, first take a look at the source code of SpringApplicationRunListener:

SpringApplicationRunListener has only one implementation class: EventPublishingRunListener.

The code at ① will only get an instance of EventPublishingRunListener. Let's take a look at the contents of the starting () method:

Following this logic, you can find listeners.environmentPrepared (environment) in the source code of the prepareEnvironment () method at ②.

This is the second method of the SpringApplicationRunListener interface, and as you might expect, environmentPrepared () publishes another event ApplicationEnvironmentPreparedEvent. I don't need to say more about what will happen next.

② creates and configures the Environment that the current application will use.

Environment is used to describe the current environment in which the application is running, abstracting two aspects: configuration files (profile) and properties (properties).

Experienced developers must be familiar with these two things: different environments (eg: production environment, pre-release environment) can use different configuration files, while properties can be obtained from configuration files, environment variables, command line parameters, and other sources.

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 or not, create it if it is not (create a StandardServletEnvironment if it is a Web project, otherwise create a StandardEnvironment).

Configure Environment: configure profile and properties.

Call the environmentPrepared () method of SpringApplicationRunListener to inform the event listener that the Environment of the application is ready.

The ③ Spring Boot application outputs something like this when it starts:

If you want to change this thing into your own graffiti, you can study the implementation of Banner, and this task will be left to you.

④ creates different ApplicationContext containers depending on whether it is a Web project.

⑤ creates a series of FailureAnalyzer.

The creation process is still to get all the class that implements the FailureAnalyzer interface through SpringFactoriesLoader, and then create the corresponding instances. FailureAnalyzer is used to analyze faults and provide relevant diagnostic information.

⑥ initializes ApplicationContext.

The main tasks are as follows:

Set the prepared Environment to ApplicationContext.

Iterate through all the ApplicationContextInitializer's initialize () methods to further process the ApplicationContext that has been created.

Call the contextPrepared () method of SpringApplicationRunListener to inform all listeners that ApplicationContext is ready.

Load all bean into the container.

Call the contextLoaded () method of SpringApplicationRunListener to notify all listeners that the ApplicationContext has been loaded.

⑦ calls the refresh () method of ApplicationContext to complete a process that is available to the IOC container.

From the name, it means refreshing the container, so what is refreshing? Is to intervene in the startup of the container and contact the contents of the * * section. So how to refresh it?

Take a look at the following code:

Take a look at the implementation of this method:

Get all the BeanFactoryPostProcessor to do some extra work 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 three Processor:

There are not so many BeanFactoryPostProcessor implementation classes, why are there only three here?

Because of the various ApplicationContextInitializer and ApplicationListener obtained by the initialization process, only the above three operations are similar to the following:

Then you can enter the PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors () method.

In addition to traversing the above three BeanFactoryPostProcessor processes, this method also gets the bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor of type BeanDefinitionRegistryPostProcessor, and the corresponding Class is ConfigurationClassPostProcessor.

ConfigurationClassPostProcessor is used to parse and process various annotations, including:

@ Configuration

@ ComponentScan

@ Import

@ PropertySource

@ ImportResource

@ Bean

When the @ import annotation is processed, EnableAutoConfigurationImportSelector.selectImports () in this section is called to complete the automatic configuration function. I won't talk about anything else here. If you are interested, you can refer to reference 6.

⑧ finds whether CommandLineRunner and ApplicationRunner are registered in the current context, and if so, traverses and executes them.

⑨ executes the finished () method of all SpringApplicationRunListener.

This is the whole startup process of Spring Boot, and its core is to add various extension points on the basis of initialization and startup of the Spring container.

These extension points include ApplicationContextInitializer, ApplicationListener, various BeanFactoryPostProcessor, and so on.

Thank you for reading, the above is the content of "what is the Spring Boot startup process and how to achieve automatic configuration". After the study of this article, I believe you have a deeper understanding of what the Spring Boot startup process is and how to achieve automatic configuration, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report