我們從主方法啟動(dòng)的入口開始,如下:
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
run方法主要干了兩件事,一件是創(chuàng)建 SpringApplication 并進(jìn)行初始化,初始化如下圖:
另一件是run的執(zhí)行,我們重點(diǎn)看run的執(zhí)行流程短荐,一路點(diǎn)擊run方法,直到:
這個(gè)就是run方法啟動(dòng)的主流程了叹哭,下面一步步來看:
-
創(chuàng)建秒表計(jì)時(shí)器忍宋,開始計(jì)時(shí)
StopWatch stopWatch = new StopWatch(); stopWatch.start();
-
配置Headless屬性
private void configureHeadlessProperty() { System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless))); }
這里同一個(gè)屬性,取了有設(shè)置回去话速,看起來一模一樣讶踪,其實(shí)是因?yàn)?getProperty帶兩個(gè)參數(shù)的時(shí)候,會(huì)給默認(rèn)值泊交,所以再設(shè)置一次就能保證該屬性都有值
-
從META-INF/spring.factories找到所有SpringApplicationRunListener的監(jiān)聽器乳讥,啟動(dòng)監(jiān)聽器
SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass);
-
配置環(huán)境 ConfigurableEnvironment,加入到監(jiān)聽器對象中
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
點(diǎn)擊prepareEnvironment方法進(jìn)去廓俭,方法里首先創(chuàng)建環(huán)境對象:
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
接著配置環(huán)境:
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
而 configureEnvironment()方法云石,會(huì)進(jìn)行以下兩個(gè)操作:
// 加載配置源參數(shù)和命令行屬性 this.configurePropertySources(environment, args); // 配置當(dāng)前activede的描述文件 this.configureProfiles(environment, args);
回到prepareEnvironment方法:
準(zhǔn)備系統(tǒng)環(huán)境:
listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
接下來bindToSpringApplication()方法:
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
} catch (Exception var3) {
throw new IllegalStateException("Cannot bind to SpringApplication", var3);
}
}
將配置文件中的spring.main開頭的配置信息綁定到SpringApplication類對應(yīng)的屬性中。
再回到run主流程中:
- configureIgnoreBeanInfo
設(shè)置忽略掉的bean
-
接下來是打印 banner:
Banner printedBanner = this.printBanner(environment);
-
接著這一步比較重要研乒,就是創(chuàng)建容器:
context = this.createApplicationContext();
接下來是prepareContext方法
具體如下:
// 將環(huán)境變量設(shè)置到容器中
context.setEnvironment(environment);
// 對 ApplicationContext進(jìn)行后置處理汹忠,設(shè)置beanNameGenerator、resourceLoader、classLoader和conversionService
this.postProcessApplicationContext(context);
// 獲取之前獲取到的所有 initializer 類型的類宽菜,并進(jìn)行初始化
this.applyInitializers(context);
// 通知監(jiān)聽器context準(zhǔn)備完成
listeners.contextPrepared(context);
// 打印profile信息
this.logStartupProfileInfo(context);
// 注冊一個(gè)應(yīng)用參數(shù)實(shí)例
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 注冊 printedBanner實(shí)例
beanFactory.registerSingleton("springBootBanner", printedBanner);
// 設(shè)置是否允許同名bean谣膳,默認(rèn)false
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
// 獲取,加載啟動(dòng)類铅乡,注入到容器中
Set<Object> sources = this.getAllSources();
this.load(context, sources.toArray(new Object[0]));
//觸發(fā)監(jiān)聽器继谚,調(diào)用每個(gè) SpringApplicationRunListerner 的 contextLoaded 方法
listeners.contextLoaded(context);
-
refreshContext 刷新容器,完成組件的掃描阵幸、創(chuàng)建花履、加載等
this.refreshContext(context);
-
返回容器
return context;
總流程圖如下: