[SpringBoot3.0 source code] start process source code analysis

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

Step into the run method:

insert image description here

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:

insert image description here

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

insert image description here
Then go back and call:

listeners.starting(bootstrapContext, this.mainApplicationClass);

Step into the starting method:

insert image description here
Step into the doWithListeners method:

insert image description here
First the listenerAction is called: (listener) -> listener.starting(bootstrapContext)

Step into the starting method:

insert image description here

Step into the multicastInitialEvent method:

insert image description here
Stepping into the refreshApplicationListeners method:
These 7 listeners are what we loaded in earlier:
insert image description here
Call this.initialMulticaster::addApplicationListenermethod:
Explicitly remove the target of the proxy (if registered) to avoid double calls to the same listener.
Add to the set collection of applicationListeners.
insert image description here
Return after execution:

insert image description here
Step into the multicastEvent method:

insert image description here
Step into the invokeListener method:

insert image description here
doInvokeListener

insert image description here
listener.onApplicationEvent(event);

insert image description here
onApplicationStartingEvent

insert image description here
beforeInitialize

insert image description here
Finally back here:

insert image description here
insert image description here

Encapsulate command-line arguments

insert image description here

Command line parameter configuration:
insert image description here

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

insert image description here

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

insert image description here

Create Environment objects of different implementations according to different web types, and read java environment variables and system environment variables

ConfigurableEnvironment environment = getOrCreateEnvironment();

insert image description here

// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());

insert image description here

// 将@PropertieSource的配置信息 放在第一位,它的优先级是最低的
ConfigurationPropertySources.attach(environment);

insert image description here

// 发布了ApplicationEnvironmentPreparedEvent的监听器,读取了全局配置文件
listeners.environmentPrepared(bootstrapContext, environment);

Finally call the onApplicationEnvironmentPreparedEvent method:

insert image description here

Step into the postProcessEnvironment method:

insert image description here
Step into the processAndApply method:

insert image description here

Step into the applyToEnvironment method:
insert image description here

// 将所有spring.main 开头的配置信息绑定到SpringApplication中
bindToSpringApplication(environment);

insert image description here

print banner

// 打印Banner
Banner printedBanner = printBanner(environment);

Step into the printBanner method:

insert image description here
Step into the print method:

insert image description here
Step into the getBanner method:

insert image description here
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.

insert image description here

After getting the banner, it will output:

banner.printBanner(environment, sourceClass, out);

insert image description here

Step into this method and output the banner:

insert image description here

Create context container

// 据webApplicationType创建不同的Spring上下文容器(有三种)
context = createApplicationContext();

insert image description here

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.

insert image description here

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:

"Java Core Technology"

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:

insert image description here
Finally publish:

insert image description here

Execute a specific run method

// 获取Spring容器中的ApplicationRunner/CommandLineRunner类型的Bean,并执行run方法
callRunners(context, applicationArguments);

Introduction to ApplicationRunner and CommandLineRunner

insert image description here


The follow-up is to publish the ApplicationReadyEvent event and the AvailabilityChangeEvent event, and handle the exception.

Guess you like

Origin blog.csdn.net/CSDN_SAVIOR/article/details/129019377