Article directory
-
- Initialize DefaultBootstrapContext
- Enable Headless Mode
- Get the listener and start it
- Encapsulate command-line arguments
- prepare the environment
- print banner
- Create context container
- Pre-initialize the context container
- Refresh the Spring container
- print start time
- post event
- Execute a specific run method
The previous article "[SpringBoot3.0 Source Code] Source Code Analysis of the Startup Process • Part 1" mainly explained new SpringApplication()
how to set up some initializers and listeners. Next, we will explain how to call run
methods.
Step into the run method:
public ConfigurableApplicationContext run(String... args) {
// 记录时间
long startTime = System.nanoTime();
// 利用BootstrapRegistryInitializer初始化DefaultBootstrapContext对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 开启了Headless模式
configureHeadlessProperty();
// 获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布ApplicationStartingEvent事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 根据命令行参数,实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 打印Banner
Banner printedBanner = printBanner(environment);
// 据webApplicationType创建不同的Spring上下文容器(有三种)
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 预初始化spring上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新Spring容器
refreshContext(context);
afterRefresh(context, applicationArguments);
// 打印启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件
listeners.started(context, timeTakenToStartup);
// 获取Spring容器中的ApplicationRunner/CommandLineRunner类型的Bean,并执行run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
// 发布ApplicationFailedEvent事件
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
if (context.isRunning()) {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 发布ApplicationReadyEvent事件和AvailabilityChangeEvent事件
listeners.ready(context, timeTakenToReady);
}
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
// 发布ApplicationFailedEvent事件
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
Initialize DefaultBootstrapContext
Step into the createBootstrapContext method:
Enable Headless Mode
Headless mode is a system configuration that lacks a display screen, keyboard, or mouse.
Step into the configureHeadlessProperty method:
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
To enable headless mode, you need to use the setProperty method to set the corresponding system properties.
System.setProperty(“java.awt.headless”,”true”)
If you want to use headless and traditional environments in the same program, you can use the following command line to do it:
java -Djava.awt.headless=true
Get the listener and start it
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
To get SpringApplicationRunListeners, SpringBoot provides an EventPublishingRunListener, which implements the SpringApplicationRunListener interface.
Spring will use this class to publish an ApplicationContextInitializedEvent event, which can be monitored by defining an ApplicationListener.
Step into the getRunListeners method: call getSpringFactoriesInstances
the method to get the listener. This method has been mentioned earlier, because it has been put into the m cache before, so here you can get the value according to the parameters.
Finally, a SpringApplicationRunListener instance is returned.
Then go back and call:
listeners.starting(bootstrapContext, this.mainApplicationClass);
Step into the starting method:
Step into the doWithListeners method:
First the listenerAction is called: (listener) -> listener.starting(bootstrapContext)
Step into the starting method:
Step into the multicastInitialEvent method:
Stepping into the refreshApplicationListeners method:
These 7 listeners are what we loaded in earlier:
Call this.initialMulticaster::addApplicationListener
method:
Explicitly remove the target of the proxy (if registered) to avoid double calls to the same listener.
Add to the set collection of applicationListeners.
Return after execution:
Step into the multicastEvent method:
Step into the invokeListener method:
doInvokeListener
listener.onApplicationEvent(event);
onApplicationStartingEvent
beforeInitialize
Finally back here:
Encapsulate command-line arguments
Command line parameter configuration:
DefaultApplicationArguments
Construction method:
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
prepare the environment
Read environment variables (environment variables of the operating system/environment variables of the JVM), and read configuration file information (based on the listener, an ApplicationEnvironmentPreparedEvent event will be published using EventPublishingRunListener. By default, there will be an EnvironmentPostProcessorApplicationListener to handle this event. Of course, you can also use the Define ApplicationListener to handle this event. When ApplicationListener receives this event, it will parse the application.properties and application.yml files and add them to the Environment.
Step into the prepareEnvironment method:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 根据不同的web类型创建不同实现的Environment对象,读取:java环境变量和系统环境变量
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将@PropertieSource的配置信息 放在第一位,它的优先级是最低的
ConfigurationPropertySources.attach(environment);
// 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
// 将所有spring.main 开头的配置信息绑定到SpringApplication中
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 更新PropertySources配置
ConfigurationPropertySources.attach(environment);
return environment;
}
Create Environment objects of different implementations according to different web types, and read java environment variables and system environment variables
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将@PropertieSource的配置信息 放在第一位,它的优先级是最低的
ConfigurationPropertySources.attach(environment);
// 发布了ApplicationEnvironmentPreparedEvent的监听器,读取了全局配置文件
listeners.environmentPrepared(bootstrapContext, environment);
Finally call the onApplicationEnvironmentPreparedEvent method:
Step into the postProcessEnvironment method:
Step into the processAndApply method:
Step into the applyToEnvironment method:
// 将所有spring.main 开头的配置信息绑定到SpringApplication中
bindToSpringApplication(environment);
print banner
// 打印Banner
Banner printedBanner = printBanner(environment);
Step into the printBanner method:
Step into the print method:
Step into the getBanner method:
Step into the getTextBanner method: first get the value of spring.banner.location, if not, it defaults to the root path, and outputs the banner.txt file.
After getting the banner, it will output:
banner.printBanner(environment, sourceClass, out);
Step into this method and output the banner:
Create context container
// 据webApplicationType创建不同的Spring上下文容器(有三种)
context = createApplicationContext();
Pre-initialize the context container
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
Step into the prepareContext method:
First obtain all ApplicationContextInitializers, and call the initialize method in a loop.
Get beanFactory.
Check the bean, if there is a duplicate bean, an exception will be thrown.
Register the startup class into the Spring container.
Refresh the Spring container
The articles in the previous chapters here focus on bean loading, instantiation, initialization, aop, transactions, and tomcat startup.
You can move to the column to view:
print start time
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
post event
// 发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件
listeners.started(context, timeTakenToStartup);
Finally came to this method to publish the event:
Finally publish:
Execute a specific run method
// 获取Spring容器中的ApplicationRunner/CommandLineRunner类型的Bean,并执行run方法
callRunners(context, applicationArguments);
Introduction to ApplicationRunner and CommandLineRunner
The follow-up is to publish the ApplicationReadyEvent event and the AvailabilityChangeEvent event, and handle the exception.