Java SpringBoot核心源碼詳解

SpringBoot源碼主線分析

我們要分析一個框架的源碼不可能通過一篇文章就搞定的,本文我們就來分析下SpringBoot源碼中的主線流程。先掌握SpringBoot項目啟動的核心操作,然後我們再深入每一個具體的實現細節,註:本系列源碼都以SpringBoot2.2.5.RELEASE版本來講解

1.SpringBoot啟動的入口

當我們啟動一個SpringBoot項目的時候,入口程序就是main方法,而在main方法中就執行瞭一個run方法。

@SpringBootApplication
public class StartApp {
	public static void main(String[] args) {
		SpringApplication.run(StartApp.class);
	}
}

2.run方法

然後我們進入run()方法中看。代碼比較簡單

	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		// 調用重載的run方法,將傳遞的Class對象封裝為瞭一個數組
		return run(new Class<?>[] { primarySource }, args);
	}

調用瞭重載的一個run()方法,將我們傳遞進來的類對象封裝為瞭一個數組,僅此而已。我們再進入run()方法。

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		// 創建瞭一個SpringApplication對象,並調用其run方法
		// 1.先看下構造方法中的邏輯
		// 2.然後再看run方法的邏輯
		return new SpringApplication(primarySources).run(args);
	}

在該方法中創建瞭一個SpringApplication對象。同時調用瞭SpringApplication對象的run方法。這裡的邏輯有分支,先看下SpringApplication的構造方法中的邏輯

3.SpringApplication構造器

我們進入SpringApplication的構造方法,看的核心代碼為

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 傳遞的resourceLoader為null
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		// 記錄主方法的配置類名稱
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 記錄當前項目的類型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 加載配置在spring.factories文件中的ApplicationContextInitializer對應的類型並實例化
		// 並將加載的數據存儲在瞭 initializers 成員變量中。
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 初始化監聽器 並將加載的監聽器實例對象存儲在瞭listeners成員變量中
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 反推main方法所在的Class對象 並記錄在瞭mainApplicationClass對象中
		this.mainApplicationClass = deduceMainApplicationClass();
	}

在本方法中完成瞭幾個核心操作

1.推斷當前項目的類型

2.加載配置在spring.factories文件中的ApplicationContextInitializer中的類型並實例化後存儲在瞭initializers中。

3.和2的步驟差不多,完成監聽器的初始化操作,並將實例化的監聽器對象存儲在瞭listeners成員變量中

4.通過StackTrace反推main方法所在的Class對象

上面的核心操作具體的實現細節我們在後面的詳細文章會給大傢剖析

4.run方法

接下來我們在回到SpringApplication.run()方法中。

	public ConfigurableApplicationContext run(String... args) {
		// 創建一個任務執行觀察器
		StopWatch stopWatch = new StopWatch();
		// 開始執行記錄執行時間
		stopWatch.start();
		// 聲明 ConfigurableApplicationContext 對象
		ConfigurableApplicationContext context = null;
		// 聲明集合容器用來存儲 SpringBootExceptionReporter 啟動錯誤的回調接口
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 設置瞭一個名為java.awt.headless的系統屬性
		// 其實是想設置該應用程序,即使沒有檢測到顯示器,也允許其啟動.
		//對於服務器來說,是不需要顯示器的,所以要這樣設置.
		configureHeadlessProperty();
		// 獲取 SpringApplicationRunListener 加載的是 EventPublishingRunListener
		// 獲取啟動時到監聽器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 觸發啟動事件
		listeners.starting();
		try {
			// 構造一個應用程序的參數持有類
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 創建並配置環境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// 配置需要忽略的BeanInfo信息
			configureIgnoreBeanInfo(environment);
			// 輸出的Banner信息
			Banner printedBanner = printBanner(environment);
			// 創建應用上下文對象
			context = createApplicationContext();
			// 加載配置的啟動異常處理器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 刷新前操作
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 刷新應用上下文 完成Spring容器的初始化
			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);
		}
		// 返回上下文對象--> Spring容器對象
		return context;
	}

在這個方法中完成瞭SpringBoot項目啟動的很多核心的操作,我們來總結下上面的步驟

  • 創建瞭一個任務執行的觀察器,統計啟動的時間
  • 聲明ConfigurableApplicationContext對象
  • 聲明集合容器來存儲SpringBootExceptionReporter即啟動錯誤的回調接口
  • 設置java.awt.headless的系統屬性
  • 獲取我們之間初始化的監聽器(EventPublishingRunListener),並觸發starting事件
  • 創建ApplicationArguments這是一個應用程序的參數持有類
  • 創建ConfigurableEnvironment這時一個配置環境的對象
  • 配置需要忽略的BeanInfo信息
  • 配置Banner信息對象
  • 創建對象的上下文對象
  • 加載配置的啟動異常的回調異常處理器
  • 刷新應用上下文,本質就是完成Spring容器的初始化操作
  • 啟動結束記錄啟動耗時
  • 完成對應的事件廣播
  • 返回應用上下文對象。

到此SpringBoot項目的啟動初始化的代碼的主要流程就介紹完成瞭。細節部分後面詳細講解。

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: