深刻理解SpringBoot核心原理(三)-------- run()啓動源碼全過程分析

1、前言

  在上一篇咱們瞭解到 new SpringApplication(primarySources)實例初始化源碼的加載過程,經過走跟源碼分析了基本初始化過程以下:
html

1.資源初始化資源加載器爲 nulljava

2.斷言主要加載資源類不能爲 null,不然報錯spring

3.初始化主要加載資源類集合並去重併發

4.推斷當前 WEB 應用類型app

5.設置應用上下文初始化器less

6.設置監聽器spring-boot

7.推斷主入口應用類 源碼分析

若是,各位同窗有遺忘的,能夠去複習一下上篇文章深刻理解SpringBoot核心原理(二)--------初始化流程(run方法)。 那麼,這篇咱們繼續往下面分析其核心 run 方法。post

2、SpringApplication 實例 run 方法運行過程


下面繼續來分析SpringApplication對象的run方法實現過程以及運行原理。

仍是跟以前的分析流程同樣,先來看一下run方法裏面整體的流程實現:學習

public ConfigurableApplicationContext run(String... args) {
		// 一、建立並啓動計時監控類
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		// 二、初始化應用上下文和異常報告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		
		// 三、設置系統屬性 `java.awt.headless` 的值,默認值爲:true
		configureHeadlessProperty();
		
		// 四、建立全部 Spring 運行監聽器併發布應用啓動事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			
			// 五、初始化默認應用參數類
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			
			// 六、根據運行監聽器和應用參數來準備 Spring 環境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			
			// 七、建立 Banner 打印類
			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);
			
			 // 1三、中止計時監控類
			stopWatch.stop();
			
			// 1四、輸出日誌記錄執行主類名、時間信息
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			
			// 1五、發佈應用上下文啓動完成事件
			listeners.started(context);
			
			// 1六、執行全部 Runner 運行器
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			
			// 1七、發佈應用上下文就緒事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		// 1八、返回應用上下文
		return context;
	}
複製代碼

3、run 方法運行過程分解

3.1 建立並啓動計時監控類

StopWatch stopWatch = new StopWatch();
		stopWatch.start();
複製代碼

進入start()方法以下:

/** * Start an unnamed task. The results are undefined if {@link #stop()} * or timing methods are called without invoking this method. * @see #stop() */
	public void start() throws IllegalStateException {
		start("");
	}

	/** * Start a named task. The results are undefined if {@link #stop()} * or timing methods are called without invoking this method. * @param taskName the name of the task to start * @see #stop() */
	public void start(String taskName) throws IllegalStateException {
		if (this.currentTaskName != null) {
			throw new IllegalStateException("Can't start StopWatch: it's already running");
		}
		this.currentTaskName = taskName;
		this.startTimeMillis = System.currentTimeMillis();
	}
複製代碼

首先記錄了當前任務的名稱,默認爲空字符串,而後記錄當前 Spring Boot 應用啓動的開始時間。

3.2 初始化應用上下文和異常報告集合

ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
複製代碼

3.3 設置系統屬性 java.awt.headless 的值

configureHeadlessProperty();
複製代碼

至於爲何設置這個屬性值爲true,能夠參考下面這篇文章:www.cnblogs.com/princessd82…

3.4 建立全部 Spring 運行監聽器併發布應用啓動事件

SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
複製代碼

進去看一下建立spring運行監聽器的相關源碼:

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}
SpringApplicationRunListeners {
        ......
		SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
        ......
}
複製代碼

建立邏輯和以前實例化初始化器和監聽器的同樣,同樣調用的是getSpringFactoriesInstances 方法來獲取配置的監聽器名稱並實例化全部的類。SpringApplicationRunListener全部監聽器配置在 spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories 這個配置文件裏面:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
複製代碼

3.5 初始化默認應用參數類

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
複製代碼

3.6 根據運行監聽器和應用參數來準備 Spring 環境

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
複製代碼

下面咱們主要來看下準備環境的 prepareEnvironment 源碼:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
		// 1.Create the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 2.Configure the environment
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
複製代碼

3.7 建立 Banner 打印類

Banner printedBanner = printBanner(environment);
複製代碼

3.8 建立應用上下文

context = createApplicationContext();
複製代碼

進去源碼,能夠知道這裏主要是根據不一樣的應用類型初始化不一樣的上下文應用類。

3.9 準備異常報告器

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
複製代碼

getSpringFactoriesInstances ------>>createSpringFactoriesInstances ------->>>邏輯和以前實例化初始化器和監聽器的同樣,同樣調用的是 getSpringFactoriesInstances 方法來獲取配置的異常類名稱並實例化全部的異常處理類。
該異常報告處理類配置在 spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories 這個配置文件裏面。

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
複製代碼

3.10 準備應用上下文

prepareContext(context, environment, listeners, applicationArguments, printedBanner);
複製代碼

接下來進入prepareContext方法:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		// 配置上下文的 bean 生成器及資源加載器
		postProcessApplicationContext(context);
		// 爲上下文應用全部初始化器
		applyInitializers(context);
		// 觸發全部 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法
		listeners.contextPrepared(context);
		// 記錄日誌
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans 啓動兩個特殊的單例bean
		context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources 加載全部資源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// 觸發全部 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
		listeners.contextLoaded(context);
	}
複製代碼

3.11 刷新應用上下文

refreshContext(context);
複製代碼

3.12 應用上下文刷新後,自定義處理

afterRefresh(context, applicationArguments);

	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}
複製代碼

3.13 中止計時監控類

stopWatch.stop();
複製代碼

計時監聽器中止,並統計一些任務執行信息。

3.14 輸出日誌記錄執行主類名、時間信息

if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
複製代碼

3.15 發佈應用上下文啓動完成事件

listeners.started(context);
複製代碼

這裏會觸發全部 SpringApplicationRunListener 監聽器的 started 事件方法。

3.16 執行全部 Runner 運行器

callRunners(context, applicationArguments);
複製代碼

執行全部ApplicationRunner以及CommandLineRunner執行器

3.17 發佈應用上下文就緒事件

listeners.running(context);
複製代碼

觸發全部 SpringApplicationRunListener 監聽器的 running 事件方法。

3.18 返回應用上下文

return context;
複製代碼

4、總結

  關於SpringBootApplication.run()啓動實例初始化以及實例加載run方法的源碼分析到此結束,分析源碼是件有點痛苦的事情,不過度析完源碼後,你會對SpringBoot是如何加載以及初始化有更全面的瞭解,固然其中也有其它的一些東西值得學習,好比Spring事件監聽,如何使用單例,自動化配置等等,最後,但願給各位同窗在學習SpringBoot的路上提供一點幫助。看完,若是以爲有收穫,但願點個贊。

相關文章
相關標籤/搜索