1.導語
//以下是一個SpringBoot應用的標準入口:
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
//SpringApplication.run 內部是這樣的:先建立一個 SpringApplication 物件,然後呼叫他的run方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
所以想要了解清楚 SpringBoot 啟動流程,相當於瞭解 SpringApplication 的 構造方法 和 run方法。
2.構造方法詳解
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判斷應用型別,會有三種情況:
// NONE:非web應用
// SERVLET:普通web應用
// REACTIVE:響應式web應用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 從 META-INF/spring.factories 檔案中獲取如下內容
// 獲取 註冊初始化器:在 run 方法中的 createBootstrapContext(); 裡會被使用
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 獲取 容器初始化器:在 run 方法中的 prepareContext( ... ); 裡會被使用
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 獲取 監聽器:在 run 方法中 listeners.xxx 時被使用
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
3.run方法詳解
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 第一步:建立監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 釋出事件:開始啟動
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 第二步:準備環境變數
// 把一系列設定整合成 environment,包括 系統變數、JVM引數、啟動引數(args)、配置檔案(application、bootstrap等)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
// 第三步:建立空容器
// 根據 new 時判斷的 web型別(webApplicationType),建立對應型別的容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 第四步:準備容器
// 把前面準備的東西注入進去,包括 環境變數(environment)、事件監聽器(listeners)、啟動引數(applicationArguments)
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 第五步:啟用容器
// 解析@SpringBootApplication註解,載入自動配置類
// 進行Bean的掃描、載入、例項化、依賴注入
// 如果是Web應用,會啟動一個內嵌式的Web伺服器比如Tomcat
refreshContext(context);
// 容器初始化後的操作,是個空方法,要使用需要繼承並重寫
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 釋出事件:已啟動
listeners.started(context, timeTakenToStartup);
// 執行啟動後的自定方法:
// 實現 ApplicationRunner、CommandLineRunner 介面
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 檢查應用是否還在執行
if (context.isRunning()) {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 釋出事件:準備就緒
listeners.ready(context, timeTakenToReady);
}
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回Bean容器
return context;
}