文章僅從源碼的角度探討springboot2.x的原理,不探討使用。
我們知道Springboot是靠着這段代碼進行啓動的。
public static void main(String[] args) {
SpringApplication.run(XxxApplication.class, args);
}
分析源碼的話,理所當然從這裏下手。
通過定位到源碼,我們發現這裏有兩步。一步是初始化SpringApplication對象,一步是調用run方法進行來完成啓動。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
對於SpringApplication初始化的源碼,主要就是對相關的屬性進行賦值。(this.xxx = xxx)
下面看一下run方法,它的內容非常簡短:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
表面上看只有三十多行代碼,其實背後的代碼量是非常高的。
上面我們大致清楚了springboot的啓動,本質就是初始化SpringApplication對象後,調用run方法。在扣細節之前,有必要整體認識一下springboot啓動做了哪些事情:
框架初始化分爲:
- 配置資源加載器
- 配置primarySources(一般是我們的啓動類)
- 應用環境的檢測(springboot1.x版本有兩種環境,標準環境和web環境,spingboot2.x添加了一種Reactive環境)
- 配置系統初始化器
- 配置應用監聽器
- 配置main方法所在類
接着就是框架的基本啓動
- 計時器開始計時
- Headless模式賦值
- 發送ApplicationStartingEvent
- 配置環境模塊
- 發送ApplicationEnvironmentPreparedEvent
- 打印banner
- 創建應用上下文對象
- 初始化失敗分析器
- 關聯springboot組件與應用上下文對象
- 發送ApplicationContextInitalizedEvent
- 加載sources到context
- 發送ApplicationPreparedEvent
- 刷新上下文(完成bean的加載)
- 計時器停止計時
- 發送ApplicationStartedEvent事件
- 調用框架啓動擴展類
- 發送ApplicationReadyEvent
通過以上步驟完成基本的啓動,後面還有框架的自動化裝配的內容:
- 收集配置文件中的配置工廠類
- 加載組件工廠
- 註冊組件內定義 bean
文字描述,可能不太直觀,這裏用圖示整體梳理一下:
下面一起扣細節吧。