In the entry class of Spring Boot, we usually SpringApplication
start the Spring Boot project by calling the run method. In this section, we will in-depth study some details of SpringApplication.
Custom SpringApplication
By default, we SpringApplication
start Spring Boot directly through the run method. In fact, we can adjust certain behaviors through some APIs.
Adjust via SpringApplication API
We create a new SpringBoot project, the Spring Boot version is 2.1.0.RELEASE, artifactId
is SpringApplication, and introduces spring-boot-starter-web
dependencies. The project structure is as follows:
We change the code of the entry class to:
SpringApplication application = new SpringApplication(DemoApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.setWebApplicationType(WebApplicationType.NONE);
application.setAdditionalProfiles("dev");
application.run(args);
By calling SpringApplication
the method, we turned off the printing of Banner, set the application environment as a non-WEB application, and specified the profiles as dev. In addition, SpringApplication
it also contains many other methods, you can check the source code or official documents for details:
Adjust via SpringApplicationBuilder API
SpringApplicationBuilder
Provides Fluent API, which can realize chain call. The following code is consistent with the above effect, but it is more convenient to write:
new SpringApplicationBuilder(DemoApplication.class)
.bannerMode(Banner.Mode.OFF)
.web(WebApplicationType.NONE)
.profiles("dev")
.run(args);
SpringApplication preparation phase
SpringApplication
The life cycle phases of the system can be roughly divided into preparation phase and operation phase.
The SpringApplication
parameterized constructor that we view through the source code :
Through the code in the parameterized constructor, we can divide SpringApplication
the preparation phase into the following steps:
Configuration source
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
This line of code in the constructor is used to load the Spring Boot Bean source we configured. Usually we use SpringApplication
or SpringApplicationBuilder
constructor directly specify the source.
The so-called Spring Boot Bean source refers to an @SpringBootApplication
annotated class, such as the entry class:
We can also change the above code to the following way:
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ApplicationResource.class);
application.run(args);
}
@SpringBootApplication
public static class ApplicationResource {
}
}
This is also feasible. Viewed SpringApplication
single parameter constructor:
Explain that in addition to configuring a single source, we can also configure multiple sources.
Infer application type
This line of this.webApplicationType = WebApplicationType.deduceFromClasspath();
code in the constructor is used to infer the current Spring Boot application type.
After Spring Boot 2.0, applications can be divided into the following three types:
-
WebApplicationType.NONE
: Non-WEB type; -
WebApplicationType.REACTIVE
: Web Reactive type; -
WebApplicationType.SERVLET
: Web Servlet type.
WebApplicationType.deduceFromClasspath()
Or according to whether there are related implementation classes in the current application ClassPath to determine which application type is, deduceFromClasspath
the source code of the method is as follows:
We can also directly by SpringApplication
the setWebApplicationType
method or SpringApplicationBuilder
the web
method used to specify the type of the current application.
Load the application context initiator
Then the next line of code is setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
used to load the application context initiator ApplicationContextInitializer
.
getSpringFactoriesInstances
The source code of the method is as follows:
The above code uses the Spring factory loading mechanism to instantiate ApplicationContextInitializer
and sort implementation classes.
So we can ApplicationContextInitializer
implement some custom operations before the Spring Boot application is initialized by implementing the interface.
For example, in com.example.demo
the new initializer
package, and then create a HelloApplicationContextInitializer
class that implements ApplicationContextInitializer
the interface:
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ConfigurableApplicationContext.id - " + applicationContext.getId());
}
}
The initialize method is implemented in the above code, and the @Order
priority is specified using annotations. Where Ordered.HIGHEST_PRECEDENCE
is equal to Integer.MIN_VALUE
, Ordered.LOWEST_PRECEDENCE
equal to Integer.MAX_VALUE
. So the smaller the value, the higher the priority.
In addition to using @Order
annotations to specify the priority, we can also specify the priority by implementing the org.springframework.core.Ordered
interface getOrder
method.
Next, let's create an HelloApplicationContextInitializer
Initializer with a lower priority ratio -- AfterHelloApplicationContextInitializer
:
public class AfterHelloApplicationContextInitializer implements ApplicationContextInitializer, Ordered {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("AfterHelloApplicationContextInitializer: " + applicationContext.getId());
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
The above getOrder
method specifies the priority as the lowest priority.
After creation, we also need to configure these two implementation classes in the factory configuration file. Create a new META-INF directory under the resources directory and create a spring.factories file:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.demo.initializer.HelloApplicationContextInitializer,\
com.example.demo.initializer.AfterHelloApplicationContextInitializer
At this time, when you start the Spring Boot project, you will find that the console executes these two initializers after printing the Banner, and HelloApplicationContextInitializer
the initialize
method execution time is earlier than AfterHelloApplicationContextInitializer
the initialize
method:
Load application event listener
After loading the application context initiator, the next line of setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
code loads the application event listener. Similar to loading the event context initiator, Spring Boot is also ApplicationListener
an implementation class that is instantiated through Spring's factory method and sorted.
Since it is an event monitor, what events can it monitor? It listens to ApplicationEvent
the implementation class of the interface. Let's check which events implement this interface:
Here we take ContextClosedEvent
as an example to write the application event listeners custom Spring context monitor off event.
In com.example.demo
case the new listener
package, and then create a ContextClosedEventListener
class that implements ApplicationListener
the interface:
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("ContextClosedEvent: " + event.getApplicationContext().getId());
}
}
The above code implements the ContextClosedEvent
monitoring of events and assigns the highest priority.
Then create a lower priority than ContextClosedEventListener
the above code to implement the ContextClosedEvent
event listener AfterContextClosedEventListener
:
public class AfterContextClosedEventListener implements ApplicationListener<ContextClosedEvent>, Ordered {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("AfterContextClosedEventr: " + event.getApplicationContext().getId());
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
Finally, don't forget to configure in the Spring factory configuration file:
# Application Listeners
org.springframework.context.ApplicationListener=\
com.example.demo.listener.ContextClosedEventListener,\
com.example.demo.listener.AfterContextClosedEventListener
Specify the environment as a non-WEB environment in the Spring Boot entry class (so that the application will be closed immediately after startup):
new SpringApplicationBuilder(DemoApplication.class)
.web(WebApplicationType.NONE)
.run(args);
Run the Spring Boot entry class, the console output is as follows:
Infer the entry class
Then the next line of code in the constructor is this.mainApplicationClass = deduceMainApplicationClass();
used to infer the entry class for running the Spring Boot application. View deduceMainApplicationClass
method source code:
The main logic of the code is to determine the actual entry class based on the execution stack of the Main thread.
After the introduction of the preparation phase is completed, the next step is to introduce the operation phase.
SpringApplication runtime phase
SpringApplication operational phase corresponds to SpringApplication
the run
way we view its source code:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
The operation phase can be roughly divided into the following processes:
Turn on time monitoring
run
The two lines of code at the beginning of the method are used to turn on time monitoring:
StopWatch stopWatch = new StopWatch();
stopWatch.start();
The above code is used to open the Spring Boot application startup time monitoring, and stopWatch.stop();
the complete startup time can be calculated with the following .
Open running listener
run
These few lines of code of the method are used to load the Spring Application Run Listener (SpringApplicationRunListener):
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
getRunListeners
Method source code:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
The above code SpringFactoriesLoader
finds all SpringApplicationRunListener
the declared implementation classes by retrieving META-INF/spring.factories , instantiates them, and then assembles them into the List<SpringApplicationRunListener>
running listener collection.
listeners.started();
It is used to traverse all SpringApplicationRunListener
the implementation classes in the set of running listeners and call their starting
methods one by one to broadcast that the Spring Boot application is about to start.
In Spring Boot, the SpringApplicationRunListener
interface is used to monitor the entire Spring Boot application life cycle. The code is as follows:
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void started(ConfigurableApplicationContext context);
void running(ConfigurableApplicationContext context);
void failed(ConfigurableApplicationContext context, Throwable exception);
}
These methods correspond to the various stages of the Spring Boot application life cycle:
Method name | Corresponding life cycle | Spring Boot initial version |
---|---|---|
starting() | Spring application just started | 1.0 |
environmentPrepared(ConfigurableEnvironment) | ConfigurableEnvironment is ready, allow it to be adjusted | 1.0 |
contextPrepared(ConfigurableApplicationContext) | ConfigurableApplicationContext is ready, allowing it to be adjusted | 1.0 |
contextLoaded(ConfigurableApplicationContext) | ConfigurableApplicationContext has been loaded, but it has not yet started | 1.0 |
started(ConfigurableApplicationContext) | ConfigurableApplicationContext has been started, and the Spring Bean has been initialized at this time | 2.0 |
running(ConfigurableApplicationContext) | Spring application is running | 2.0 |
failed(ConfigurableApplicationContext,Throwable) | Spring application failed to run | 2.0 |
We have com.example.demo.linstener
a case custom SpringApplicationRunListener
interface implementation class HelloSpringApplicationRunListener
:
public class HelloApplicationRunListener implements SpringApplicationRunListener {
public HelloApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
System.out.println("HelloApplicationRunListener starting......");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
}
@Override
public void started(ConfigurableApplicationContext context) {
}
@Override
public void running(ConfigurableApplicationContext context) {
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
Through this implementation class, we can output in the console when the Spring Boot application just starts HelloApplicationRunListener starting......
.
Because it is implemented based on Spring's factory method, we need to configure this implementation class in the spring.factories file:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.run.HelloApplicationRunListener
Start the Spring Boot application and you can see the following output in the console:
Create Environment
run
This line of code in the method is used to create and configure the Environment to be used by the current SpringBoot application (including configuring the PropertySource and Profile to be used):
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
We have inferred the application type in the preparation stage. Here, we only need to create the corresponding application environment according to the corresponding application type. The corresponding relationship between the type and the environment is as follows:
-
Web Reactive: StandardReactiveWebEnvironment
-
Web Servlet: StandardServletEnvironment
-
非 Web: StandardEnvironment
prepareEnvironment
Will be executed in the method listeners.environmentPrepared(environment);
, used to traverse and call SpringApplicationRunListener
the environmentPrepared()
methods of all implementation classes, and the broadcast Environment is ready.
Whether to print banner
run
This line of code in the method will determine whether to print the Banner according to our configuration:
Banner printedBanner = printBanner(environment);
Create Context
run
This line of code in the method is used to create ApplicationContext
:
context = createApplicationContext();
Different environments correspond to different ApplicationContext
:
-
Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
-
Web Servlet: AnnotationConfigServletWebServerApplicationContext
-
非 Web: AnnotationConfigApplicationContext
Assembly Context
run
This line of code in the method is used to assemble the Context:
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
prepareContext
The source code of the method is as follows:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
prepareContext
The method starts with ApplicationContext
loading the environment, and then further encapsulates it applyInitializers
by executing ApplicationContextInitializer
the method one by one , and calls all the methods of the implementation class, and the broadcast ApplicationContext is ready.initialize
ApplicationContext
SpringApplicationRunListener
contextPrepared
After initializing the IOC container, and calling SpringApplicationRunListener
the contextLoaded
method of the implementation class , the broadcast ApplicationContext
loading is completed, including @EnableAutoConfiguration
various automatic configuration classes imported through the import.
Refresh Context
run
This process line for initializing all auto configuration class, and calls ApplicationContext
the refresh
method:
refreshContext(context);
Broadcast application has started
run
This line of code in the method is used to broadcast that the Spring Boot application has started:
listeners.started(context);
started
The method will call all SpringApplicationRunListener
the finished
way broadcast SpringBoot application has been successfully launched.
Execute Runner
run
This line of code in the method callRunners(context, applicationArguments);
traverses all the implementation classes of the ApplicationRunner
sum CommandLineRunner
and executes its run
method. We can implement our own ApplicationRunner
or CommandLineRunner
to extend the startup process of Spring Boot.
We are com.example.demo
under the new runner
package, and then create an ApplicationRunner
implementation class HelloApplicationRunner
:
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
System.out.println("HelloApplicationRunner: hello spring boot");
}
}
Here we need to HelloApplicationRunner
use @Component
annotations to register them in the IOC container.
Then create an CommandLineRunner
implementation class HelloCommandLineRunner
:
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("HelloCommandLineRunner: hello spring boot");
}
}
Start the Spring Boot application, you can see the following output just after the application is started:
Broadcast application is running
run
This line of code listeners.running(context);
in SpringApplicationRunListener
the running
method is used to call the method to broadcast that the Spring Boot application is running.
When run
an exception occurs in the operation of handleRunFailure
the method, the method will be called to handle the exception, and the method that will listeners.failed(context, exception);
be called SpringApplicationRunListener
in the failed
method will broadcast the application startup failure and spread the exception.
All the above broadcast events are broadcast using
ApplicationEventMulticaster
the implementation class of Spring's application event broadcaster interfaceSimpleApplicationEventMulticaster
.