javascript
Spring Boot 2.x 启动全过程源码分析(全)
上篇《Spring Boot 2.x 啟動(dòng)全過(guò)程源碼分析(一)入口類剖析》我們分析了 Spring Boot 入口類 SpringApplication 的源碼,并知道了其構(gòu)造原理,這篇我們繼續(xù)往下面分析其核心 run 方法。
SpringApplication 實(shí)例 run 方法運(yùn)行過(guò)程
?
?
上面分析了 SpringApplication 實(shí)例對(duì)象構(gòu)造方法初始化過(guò)程,下面繼續(xù)來(lái)看下這個(gè) SpringApplication 對(duì)象的 run 方法的源碼和運(yùn)行流程。
public?ConfigurableApplicationContext?run(String...?args)?{//?1、創(chuàng)建并啟動(dòng)計(jì)時(shí)監(jiān)控類StopWatch?stopWatch?=?new?StopWatch();stopWatch.start();//?2、初始化應(yīng)用上下文和異常報(bào)告集合ConfigurableApplicationContext?context?=?null;Collection<SpringBootExceptionReporter>?exceptionReporters?=?new?ArrayList<>();//?3、設(shè)置系統(tǒng)屬性?`java.awt.headless`?的值,默認(rèn)值為:trueconfigureHeadlessProperty();//?4、創(chuàng)建所有?Spring?運(yùn)行監(jiān)聽器并發(fā)布應(yīng)用啟動(dòng)事件SpringApplicationRunListeners?listeners?=?getRunListeners(args);listeners.starting();try?{//?5、初始化默認(rèn)應(yīng)用參數(shù)類ApplicationArguments?applicationArguments?=?new?DefaultApplicationArguments(args);//?6、根據(jù)運(yùn)行監(jiān)聽器和應(yīng)用參數(shù)來(lái)準(zhǔn)備?Spring?環(huán)境ConfigurableEnvironment?environment?=?prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);//?7、創(chuàng)建?Banner?打印類Banner?printedBanner?=?printBanner(environment);//?8、創(chuàng)建應(yīng)用上下文context?=?createApplicationContext();//?9、準(zhǔn)備異常報(bào)告器exceptionReporters?=?getSpringFactoriesInstances(SpringBootExceptionReporter.class,new?Class[]?{?ConfigurableApplicationContext.class?},?context);//?10、準(zhǔn)備應(yīng)用上下文prepareContext(context,?environment,?listeners,?applicationArguments,printedBanner);//?11、刷新應(yīng)用上下文refreshContext(context);//?12、應(yīng)用上下文刷新后置處理afterRefresh(context,?applicationArguments);//?13、停止計(jì)時(shí)監(jiān)控類stopWatch.stop();//?14、輸出日志記錄執(zhí)行主類名、時(shí)間信息if?(this.logStartupInfo)?{new?StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(),?stopWatch);}//?15、發(fā)布應(yīng)用上下文啟動(dòng)完成事件listeners.started(context);//?16、執(zhí)行所有?Runner?運(yùn)行器callRunners(context,?applicationArguments);}catch?(Throwable?ex)?{handleRunFailure(context,?ex,?exceptionReporters,?listeners);throw?new?IllegalStateException(ex);}try?{//?17、發(fā)布應(yīng)用上下文就緒事件listeners.running(context);}catch?(Throwable?ex)?{handleRunFailure(context,?ex,?exceptionReporters,?null);throw?new?IllegalStateException(ex);}//?18、返回應(yīng)用上下文return?context; }所以,我們可以按以下幾步來(lái)分解 run 方法的啟動(dòng)過(guò)程。
1、創(chuàng)建并啟動(dòng)計(jì)時(shí)監(jiān)控類
StopWatch?stopWatch?=?new?StopWatch(); stopWatch.start();來(lái)看下這個(gè)計(jì)時(shí)監(jiān)控類 StopWatch 的相關(guān)源碼:
public?void?start()?throws?IllegalStateException?{start(""); }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(); }首先記錄了當(dāng)前任務(wù)的名稱,默認(rèn)為空字符串,然后記錄當(dāng)前 Spring Boot 應(yīng)用啟動(dòng)的開始時(shí)間。
2、初始化應(yīng)用上下文和異常報(bào)告集合
ConfigurableApplicationContext?context?=?null; Collection<SpringBootExceptionReporter>?exceptionReporters?=?new?ArrayList<>();3、設(shè)置系統(tǒng)屬性 `java.awt.headless` 的值
configureHeadlessProperty();設(shè)置該默認(rèn)值為:true,Java.awt.headless = true 有什么作用?
對(duì)于一個(gè) Java 服務(wù)器來(lái)說(shuō)經(jīng)常要處理一些圖形元素,例如地圖的創(chuàng)建或者圖形和圖表等。這些API基本上總是需要運(yùn)行一個(gè)X-server以便能使用AWT(Abstract Window Toolkit,抽象窗口工具集)。然而運(yùn)行一個(gè)不必要的 X-server 并不是一種好的管理方式。有時(shí)你甚至不能運(yùn)行 X-server,因此最好的方案是運(yùn)行 headless 服務(wù)器,來(lái)進(jìn)行簡(jiǎn)單的圖像處理。
參考:www.cnblogs.com/princessd8251/p/4000016.html
4、創(chuàng)建所有 Spring 運(yùn)行監(jiān)聽器并發(fā)布應(yīng)用啟動(dòng)事件
SpringApplicationRunListeners?listeners?=?getRunListeners(args); listeners.starting();來(lái)看下創(chuàng)建 Spring 運(yùn)行監(jiān)聽器相關(guān)的源碼:
private?SpringApplicationRunListeners?getRunListeners(String[]?args)?{Class<?>[]?types?=?new?Class<?>[]?{?SpringApplication.class,?String[].class?};return?new?SpringApplicationRunListeners(logger,?getSpringFactoriesInstances(SpringApplicationRunListener.class,?types,?this,?args)); }SpringApplicationRunListeners(Log?log,Collection<??extends?SpringApplicationRunListener>?listeners)?{this.log?=?log;this.listeners?=?new?ArrayList<>(listeners); }創(chuàng)建邏輯和之前實(shí)例化初始化器和監(jiān)聽器的一樣,一樣調(diào)用的是?getSpringFactoriesInstances?方法來(lái)獲取配置的監(jiān)聽器名稱并實(shí)例化所有的類。
SpringApplicationRunListener 所有監(jiān)聽器配置在?spring-boot-2.0.3.RELEASE.jar!/META-INF/spring.factories?這個(gè)配置文件里面。
#?Run?Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener5、初始化默認(rèn)應(yīng)用參數(shù)類
ApplicationArguments?applicationArguments?=?new?DefaultApplicationArguments(args);6、根據(jù)運(yùn)行監(jiān)聽器和應(yīng)用參數(shù)來(lái)準(zhǔn)備 Spring 環(huán)境
ConfigurableEnvironment?environment?=?prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment);下面我們主要來(lái)看下準(zhǔn)備環(huán)境的?prepareEnvironment?源碼:
private?ConfigurableEnvironment?prepareEnvironment(SpringApplicationRunListeners?listeners,ApplicationArguments?applicationArguments)?{//?6.1)?獲取(或者創(chuàng)建)應(yīng)用環(huán)境ConfigurableEnvironment?environment?=?getOrCreateEnvironment();//?6.2)?配置應(yīng)用環(huán)境configureEnvironment(environment,?applicationArguments.getSourceArgs());listeners.environmentPrepared(environment);bindToSpringApplication(environment);if?(this.webApplicationType?==?WebApplicationType.NONE)?{environment?=?new?EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}ConfigurationPropertySources.attach(environment);return?environment; }6.1) 獲取(或者創(chuàng)建)應(yīng)用環(huán)境
private?ConfigurableEnvironment?getOrCreateEnvironment()?{if?(this.environment?!=?null)?{return?this.environment;}if?(this.webApplicationType?==?WebApplicationType.SERVLET)?{return?new?StandardServletEnvironment();}return?new?StandardEnvironment(); }這里分為標(biāo)準(zhǔn) Servlet 環(huán)境和標(biāo)準(zhǔn)環(huán)境。
6.2) 配置應(yīng)用環(huán)境
protected?void?configureEnvironment(ConfigurableEnvironment?environment,String[]?args)?{configurePropertySources(environment,?args);configureProfiles(environment,?args); }這里分為以下兩步來(lái)配置應(yīng)用環(huán)境。
-
配置 property sources
-
配置 Profiles
這里主要處理所有 property sources 配置和 profiles 配置。
7、創(chuàng)建 Banner 打印類
Banner?printedBanner?=?printBanner(environment);這是用來(lái)打印 Banner 的處理類,這個(gè)沒什么好說(shuō)的。
8、創(chuàng)建應(yīng)用上下文
context?=?createApplicationContext();來(lái)看下?createApplicationContext()?方法的源碼:
protected?ConfigurableApplicationContext?createApplicationContext()?{Class<?>?contextClass?=?this.applicationContextClass;if?(contextClass?==?null)?{try?{switch?(this.webApplicationType)?{case?SERVLET:contextClass?=?Class.forName(DEFAULT_WEB_CONTEXT_CLASS);break;case?REACTIVE:contextClass?=?Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass?=?Class.forName(DEFAULT_CONTEXT_CLASS);}}catch?(ClassNotFoundException?ex)?{throw?new?IllegalStateException("Unable?create?a?default?ApplicationContext,?"+?"please?specify?an?ApplicationContextClass",ex);}}return?(ConfigurableApplicationContext)?BeanUtils.instantiateClass(contextClass); }其實(shí)就是根據(jù)不同的應(yīng)用類型初始化不同的上下文應(yīng)用類。
9、準(zhǔn)備異常報(bào)告器
exceptionReporters?=?getSpringFactoriesInstances(SpringBootExceptionReporter.class,new?Class[]?{?ConfigurableApplicationContext.class?},?context);邏輯和之前實(shí)例化初始化器和監(jiān)聽器的一樣,一樣調(diào)用的是?getSpringFactoriesInstances?方法來(lái)獲取配置的異常類名稱并實(shí)例化所有的異常處理類。
該異常報(bào)告處理類配置在?spring-boot-2.0.3.RELEASE.jar!/META-INF/spring.factories?這個(gè)配置文件里面。
#?Error?Reporters org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers10、準(zhǔn)備應(yīng)用上下文
prepareContext(context,?environment,?listeners,?applicationArguments,printedBanner);來(lái)看下?prepareContext()?方法的源碼:
private?void?prepareContext(ConfigurableApplicationContext?context,ConfigurableEnvironment?environment,?SpringApplicationRunListeners?listeners,ApplicationArguments?applicationArguments,?Banner?printedBanner)?{//?10.1)綁定環(huán)境到上下文context.setEnvironment(environment);//?10.2)配置上下文的?bean?生成器及資源加載器postProcessApplicationContext(context);//?10.3)為上下文應(yīng)用所有初始化器applyInitializers(context);//?10.4)觸發(fā)所有?SpringApplicationRunListener?監(jiān)聽器的?contextPrepared?事件方法listeners.contextPrepared(context);//?10.5)記錄啟動(dòng)日志if?(this.logStartupInfo)?{logStartupInfo(context.getParent()?==?null);logStartupProfileInfo(context);}//?10.6)注冊(cè)兩個(gè)特殊的單例beancontext.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);if?(printedBanner?!=?null)?{context.getBeanFactory().registerSingleton("springBootBanner",?printedBanner);}//?10.7)加載所有資源Set<Object>?sources?=?getAllSources();Assert.notEmpty(sources,?"Sources?must?not?be?empty");load(context,?sources.toArray(new?Object[0]));//?10.8)觸發(fā)所有?SpringApplicationRunListener?監(jiān)聽器的?contextLoaded?事件方法listeners.contextLoaded(context); }11、刷新應(yīng)用上下文
refreshContext(context);這個(gè)主要是刷新 Spring 的應(yīng)用上下文,源碼如下,不詳細(xì)說(shuō)明。
private?void?refreshContext(ConfigurableApplicationContext?context)?{refresh(context);if?(this.registerShutdownHook)?{try?{context.registerShutdownHook();}catch?(AccessControlException?ex)?{//?Not?allowed?in?some?environments.}} }12、應(yīng)用上下文刷新后置處理
afterRefresh(context,?applicationArguments);看了下這個(gè)方法的源碼是空的,目前可以做一些自定義的后置處理操作。
/***?Called?after?the?context?has?been?refreshed.*?@param?context?the?application?context*?@param?args?the?application?arguments*/ protected?void?afterRefresh(ConfigurableApplicationContext?context,ApplicationArguments?args)?{ }13、停止計(jì)時(shí)監(jiān)控類
stopWatch.stop(); public?void?stop()?throws?IllegalStateException?{if?(this.currentTaskName?==?null)?{throw?new?IllegalStateException("Can't?stop?StopWatch:?it's?not?running");}long?lastTime?=?System.currentTimeMillis()?-?this.startTimeMillis;this.totalTimeMillis?+=?lastTime;this.lastTaskInfo?=?new?TaskInfo(this.currentTaskName,?lastTime);if?(this.keepTaskList)?{this.taskList.add(this.lastTaskInfo);}++this.taskCount;this.currentTaskName?=?null; }計(jì)時(shí)監(jiān)聽器停止,并統(tǒng)計(jì)一些任務(wù)執(zhí)行信息。
14、輸出日志記錄執(zhí)行主類名、時(shí)間信息
if?(this.logStartupInfo)?{new?StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(),?stopWatch); }15、發(fā)布應(yīng)用上下文啟動(dòng)完成事件
listeners.started(context);觸發(fā)所有 SpringApplicationRunListener 監(jiān)聽器的 started 事件方法。
16、執(zhí)行所有 Runner 運(yùn)行器
callRunners(context,?applicationArguments); private?void?callRunners(ApplicationContext?context,?ApplicationArguments?args)?{List<Object>?runners?=?new?ArrayList<>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for?(Object?runner?:?new?LinkedHashSet<>(runners))?{if?(runner?instanceof?ApplicationRunner)?{callRunner((ApplicationRunner)?runner,?args);}if?(runner?instanceof?CommandLineRunner)?{callRunner((CommandLineRunner)?runner,?args);}} }執(zhí)行所有?ApplicationRunner?和?CommandLineRunner?這兩種運(yùn)行器,不詳細(xì)展開了。
17、發(fā)布應(yīng)用上下文就緒事件
listeners.running(context);觸發(fā)所有 SpringApplicationRunListener 監(jiān)聽器的 running 事件方法。
18、返回應(yīng)用上下文
return?context;總結(jié)
Spring Boot 的啟動(dòng)全過(guò)程源碼分析至此,分析 Spring 源碼真是一個(gè)痛苦的過(guò)程,希望能給大家提供一點(diǎn)參考和思路,也希望能給正在 Spring Boot 學(xué)習(xí)路上的朋友一點(diǎn)收獲。
總結(jié)
以上是生活随笔為你收集整理的Spring Boot 2.x 启动全过程源码分析(全)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse再次导入已经逻辑删除的工程
- 下一篇: Java面试中常问的计算机网络方面问题