免費(fèi)創(chuàng)辦網(wǎng)站印度疫情最新消息
文章目錄
- run方法核心流程
- SpringApplicationRunListener監(jiān)聽(tīng)器
- 監(jiān)聽(tīng)器的配置與加載
- SpringApplicationRunListener源碼解析
- 實(shí)現(xiàn)類EventPublishingRunListener
- 初始化ApplicationArguments
- 初始化ConfigurableEnvironment
- 獲取或創(chuàng)建環(huán)境
- 配置環(huán)境
- 打印Banner
- Spring應(yīng)用上下文的創(chuàng)建
- Spring應(yīng)用上下文的準(zhǔn)備
- Spring應(yīng)用上下文的刷新
- 調(diào)用ApplicationRunner和CommandLineRunner
run方法核心流程
在分析和學(xué)習(xí)整個(gè)run方法的源代碼及操作之前,我們先通過(guò)下圖所示的流程圖來(lái)看一下SpringApplication調(diào)用的run方法處理的核心操作都包含哪些。
上面的流程圖可以看出,SpringApplication在run方法中重點(diǎn)做了以下操作。
- 獲取監(jiān)聽(tīng)器和參數(shù)配置。
- 打印Banner信息。
- 創(chuàng)建并初始化容器。
- 監(jiān)聽(tīng)器發(fā)送通知。
當(dāng)然,除了核心操作,run方法運(yùn)行過(guò)程中還涉及啟動(dòng)時(shí)長(zhǎng)統(tǒng)計(jì)、異常報(bào)告、啟動(dòng)日志、異常處理等輔助操作。
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 {// 創(chuàng)建 ApplicationArguments 對(duì)象ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 加載屬性配置ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);// 打印bannerBanner printedBanner = this.printBanner(environment);// 創(chuàng)建容器context = this.createApplicationContext();// 異常報(bào)告器exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);// 準(zhǔn)備容器this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 初始化容器this.refreshContext(context);// 初始化容器后this.afterRefresh(context, applicationArguments);// 停止時(shí)長(zhǎng)統(tǒng)計(jì)stopWatch.stop();// 打印日志if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}// 通知監(jiān)聽(tīng)器容器啟動(dòng)完成listeners.started(context);this.callRunners(context, applicationArguments);} catch (Throwable var10) {// 異常處理this.handleRunFailure(context, var10, exceptionReporters, listeners);throw new IllegalStateException(var10);}try {// 通知監(jiān)聽(tīng)器:容器正在運(yùn)行listeners.running(context);return context;} catch (Throwable var9) {// 異常處理this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}
}
SpringApplicationRunListener監(jiān)聽(tīng)器
監(jiān)聽(tīng)器的配置與加載
SpringApplicationRunListeners 可以理解為一個(gè)SpringApplicationRunListener的容器,它將SpringApplicationRunListener的集合以構(gòu)造方法傳入,并賦值給其listeners成員變量,然后提供了針對(duì)listeners 成員變量的各種遍歷操作方法,比如,遍歷集合并調(diào)用對(duì)應(yīng)的starting、started、running等方法。
SpringApplicationRunListeners 的構(gòu)建很簡(jiǎn)單,SpringApplication中g(shù)etRunListeners方法代碼如下:
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class[]{SpringApplication.class, String[].class};return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners構(gòu)造方法的第二個(gè)參數(shù)便是SpringApplicationRunListener的集合,SpringApplication中調(diào)用構(gòu)造方法時(shí)該參數(shù)是通過(guò)getSpringFactoriesInstances方法獲取(都是一樣的套路)的,代碼如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {// 獲取類加載器ClassLoader classLoader = this.getClassLoader();// 加載監(jiān)聽(tīng)器,并放入set中Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 實(shí)例化監(jiān)聽(tīng)器List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);// 排序AnnotationAwareOrderComparator.sort(instances);return instances;
}
通過(guò)方法名便可得知,getSpringFactoriesInstances是用來(lái)獲取factories配置文件中的注冊(cè)類,并進(jìn)行實(shí)例化操作。
關(guān)于通過(guò)SpringFactoriesLoader獲取META-INF/spring.factories中對(duì)應(yīng)的配置,構(gòu)造方法章節(jié)已經(jīng)多次提到,這里不再贊述。SpringApplicationRunListener的注冊(cè)配置位于spring-boot項(xiàng)目中的spring.factories文件內(nèi),SpringBoot默認(rèn)僅有一個(gè)監(jiān)聽(tīng)器進(jìn)行了注冊(cè),關(guān)于其功能后面會(huì)專門(mén)講到。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList(names.size());Iterator var7 = names.iterator();while(var7.hasNext()) {String name = (String)var7.next();try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);T instance = BeanUtils.instantiateClass(constructor, args);instances.add(instance);} catch (Throwable var12) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);}}return instances;
}
在上面的代碼中,實(shí)例化監(jiān)聽(tīng)器時(shí)需要有一個(gè)默認(rèn)的構(gòu)造方法,且構(gòu)造方法的參數(shù)為Class<?>[]parameterTypes。我們向上追蹤該參數(shù)的來(lái)源,會(huì)發(fā)現(xiàn)該參數(shù)的值為Class數(shù)組,數(shù)組的內(nèi)容依次為SpringApplication.class和String[].class。也就是說(shuō),SpringApplicationRunListener的實(shí)現(xiàn)類必須有默認(rèn)的構(gòu)造方法,且構(gòu)造方法的參數(shù)必須依次為SpringApplication和String[]類型。
SpringApplicationRunListener源碼解析
接口SpringApplicationRunListener是SpringApplication的run方法監(jiān)聽(tīng)器。上節(jié)提到了 SpringApplicationRunListener通過(guò)SpringFactoriesLoader加載,并且必須聲明一個(gè)公共構(gòu)造函數(shù),該函數(shù)接收SpringApplication實(shí)例和String[]的參數(shù),而且每次運(yùn)行都會(huì)創(chuàng)建一個(gè)新的實(shí)例。
SpringApplicationRunListener提供了一系列的方法,用戶可以通過(guò)回調(diào)這些方法,在啟動(dòng)各個(gè)流程時(shí)加入指定的邏輯處理。
class SpringApplicationRunListeners {private final Log log;private final List<SpringApplicationRunListener> listeners;SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {this.log = log;this.listeners = new ArrayList(listeners);}public void starting() {Iterator var1 = this.listeners.iterator();while(var1.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();listener.starting();}}public void environmentPrepared(ConfigurableEnvironment environment) {Iterator var2 = this.listeners.iterator();while(var2.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();listener.environmentPrepared(environment);}}public void contextPrepared(ConfigurableApplicationContext context) {Iterator var2 = this.listeners.iterator();while(var2.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();listener.contextPrepared(context);}}public void contextLoaded(ConfigurableApplicationContext context) {Iterator var2 = this.listeners.iterator();while(var2.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();listener.contextLoaded(context);}}public void started(ConfigurableApplicationContext context) {Iterator var2 = this.listeners.iterator();while(var2.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();listener.started(context);}}public void running(ConfigurableApplicationContext context) {Iterator var2 = this.listeners.iterator();while(var2.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();listener.running(context);}}public void failed(ConfigurableApplicationContext context, Throwable exception) {Iterator var3 = this.listeners.iterator();while(var3.hasNext()) {SpringApplicationRunListener listener = (SpringApplicationRunListener)var3.next();this.callFailedListener(listener, context, exception);}}private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) {try {listener.failed(context, exception);} catch (Throwable var6) {if (exception == null) {ReflectionUtils.rethrowRuntimeException(var6);}if (this.log.isDebugEnabled()) {this.log.error("Error handling failed", var6);} else {String message = var6.getMessage();message = message != null ? message : "no error message";this.log.warn("Error handling failed (" + message + ")");}}}
}
我們通過(guò)源代碼可以看出,SpringApplicationRunListener為run方法提供了各個(gè)運(yùn)行階段的監(jiān)聽(tīng)事件處理功能。
下圖展示了在整個(gè)run方法的生命周期中SpringApplicationRunListener的所有方法所處的位置,該圖可以幫助我們更好地學(xué)習(xí)run方法的運(yùn)行流程。在前面run方法的代碼中已經(jīng)看到相關(guān)監(jiān)聽(tīng)方法被調(diào)用,后續(xù)的源代碼中也將涉及對(duì)應(yīng)方法的調(diào)用,我們可參考此圖以便理解和加深記憶。
實(shí)現(xiàn)類EventPublishingRunListener
EventPublishingRunListener是SpringBoot中針對(duì)SpringApplicationRunListener接口的唯一內(nèi)建實(shí)現(xiàn)。EventPublishingRunListener使用內(nèi)置的SimpleApplicationEventMulticaster來(lái)廣播在上下文刷新之前觸發(fā)的事件。
默認(rèn)情況下,SpringBoot在初始化過(guò)程中觸發(fā)的事件也是交由EventPublishingRunListener來(lái)代理實(shí)現(xiàn)的。
SpringBoot完成基本的初始化之后,會(huì)遍歷SpringApplication的所有ApplicationListener實(shí)例,并將它們與SimpleApplicationEventMulticaster進(jìn)行關(guān)聯(lián),方便SimpleApplicationEventMulticaster后續(xù)將事件傳遞給所有的監(jiān)聽(tīng)器。
EventPublishingRunListener針對(duì)不同的事件提供了不同的處理方法,但它們的處理流程基本相同。
public void contextLoaded(ConfigurableApplicationContext context) {ApplicationListener listener;for(Iterator var2 = this.application.getListeners().iterator(); var2.hasNext(); context.addApplicationListener(listener)) {listener = (ApplicationListener)var2.next();if (listener instanceof ApplicationContextAware) {((ApplicationContextAware)listener).setApplicationContext(context);}}this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
contextLoaded方法在發(fā)布事件之前做了兩件事:第一,遍歷application的所有監(jiān)聽(tīng)器實(shí)現(xiàn)類,如果該實(shí)現(xiàn)類還實(shí)現(xiàn)了ApplicationContextAware接口,則將上下文信息設(shè)置到該監(jiān)聽(tīng)器內(nèi);第二,將application中的監(jiān)聽(tīng)器實(shí)現(xiàn)類全部添加到上下文中。最后一步才是調(diào)用事件廣播。
也正是這個(gè)方法形成了不同事件廣播形式的分水嶺,在此方法之前執(zhí)行的事件廣播都是通過(guò)multicastEvent來(lái)進(jìn)行的,而該方法之后的方法則均采用publishEvent來(lái)執(zhí)行。這是因?yàn)橹挥械搅薱ontextLoaded方法之后,上下文才算初始化完成,才可通過(guò)它的publishEvent方法來(lái)進(jìn)行事件的發(fā)布。
初始化ApplicationArguments
監(jiān)聽(tīng)器啟動(dòng)之后,緊接著便是執(zhí)行ApplicationArguments對(duì)象的初始化,ApplicationArguments是用于提供訪問(wèn)運(yùn)行SpringApplication時(shí)的參數(shù)。
ApplicationArguments的初始化過(guò)程非常簡(jiǎn)單,只是調(diào)用了它的實(shí)現(xiàn)類DefaultApplicationArguments并傳入main方法中的args參數(shù)。
在DefaultApplicationArguments中將參數(shù)args封裝為Source對(duì)象,Source對(duì)象是基于Spring框架的SimpleCommandLinePropertySource來(lái)實(shí)現(xiàn)的。
初始化ConfigurableEnvironment
完成ApplicationArguments參數(shù)的準(zhǔn)備之后,便開(kāi)始通過(guò)prepareEnvironment方法對(duì)ConfigurableEnvironment對(duì)象進(jìn)行初始化操作。
ConfigurableEnvironment接口繼承自Environment接口和ConfigurablePropertyResolver,最終都繼承自接口PropertyResolverConfigurableEnvironment接口的主要作用是提供當(dāng)前運(yùn)行環(huán)境的公開(kāi)接口,比如配置文件profiles各類系統(tǒng)屬性和變量的設(shè)置、添加、讀取、合并等功能。
通過(guò)ConfigurableEnvironment接口中方法定義,可以更清楚地了解它的功能,代碼如下:
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {void setActiveProfiles(String... var1);void addActiveProfile(String var1);void setDefaultProfiles(String... var1);MutablePropertySources getPropertySources();Map<String, Object> getSystemProperties();Map<String, Object> getSystemEnvironment();void merge(ConfigurableEnvironment var1);
}
通過(guò)接口提供的方法,我們可以看出ConfigurableEnvironment就是圍繞著這個(gè)“環(huán)境”來(lái)提供相應(yīng)的功能,這也是為什么我們也將它稱作“環(huán)境”。
了解了ConfigurableEnvironment的功能及方法,我們回歸到SpringApplication的流程看相關(guān)源代碼。run方法中調(diào)用prepareEnvironment方法相關(guān)代碼如下:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {ConfigurableEnvironment environment = this.getOrCreateEnvironment();this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());listeners.environmentPrepared((ConfigurableEnvironment)environment);this.bindToSpringApplication((ConfigurableEnvironment)environment);if (!this.isCustomEnvironment) {environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());}ConfigurationPropertySources.attach((Environment)environment);return (ConfigurableEnvironment)environment;
}
通過(guò)以上代碼可知,prepareEnvironment進(jìn)行了以下的操作:
- 獲取或創(chuàng)建環(huán)境。
- 配置環(huán)境。
- ConfigurationPropertySources附加到指定環(huán)境:將ConfigurationPropertySources附加到指定環(huán)境中的第一位,并動(dòng)態(tài)跟蹤環(huán)境的添加或刪除(當(dāng)前版本新增了該行代碼,與最后一步操作相同)。
- 設(shè)置listener監(jiān)聽(tīng)事件:前面章節(jié)已經(jīng)講過(guò),此處主要針對(duì)準(zhǔn)備環(huán)境的監(jiān)聽(tīng)。
- 綁定環(huán)境到SpringApplication:將環(huán)境綁定到name為“spring.main”的目標(biāo)上。
- 轉(zhuǎn)換環(huán)境:判斷是否是定制的環(huán)境,如果不是定制的,則將環(huán)境轉(zhuǎn)換為StandardEnvironment。此時(shí)判斷條件isCustomEnvironment默認(rèn)為false,在后面的操作中會(huì)將其設(shè)置為true,如果為true則不再會(huì)進(jìn)行此轉(zhuǎn)換操作。
- ConfigurationPropertySources附加到指定環(huán)境:將ConfigurationPropertySources附加到指定環(huán)境中的第一位,并動(dòng)態(tài)跟蹤環(huán)境的添加或刪除操作。
獲取或創(chuàng)建環(huán)境
SpringApplication類中通過(guò)getOrCreateEnvironment方法來(lái)獲取或創(chuàng)建環(huán)境。在該方法中首先判斷環(huán)境是否為null,如果不為null則直接返回;如果為null,則根據(jù)前面推斷出來(lái)的WebApplicationType類型來(lái)創(chuàng)建指定的環(huán)境。
配置環(huán)境
在獲得環(huán)境變量對(duì)象之后,開(kāi)始對(duì)環(huán)境變量和參數(shù)進(jìn)行相應(yīng)的設(shè)置,主要包括轉(zhuǎn)換服務(wù)的設(shè)置、PropertySources的設(shè)置和activeProfiles的設(shè)置。
打印Banner
完成環(huán)境的基本處理之后,下面就是控制臺(tái)Banner的打印了。SpringBoot的Banner打印是一個(gè)比較酷炫的功能,但又顯得有些華而不實(shí),特別是打印圖片時(shí)啟動(dòng)速度會(huì)變慢。
private Banner printBanner(ConfigurableEnvironment environment) {if (this.bannerMode == Mode.OFF) {return null;} else {ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(this.getClassLoader());SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);}
}
上面的代碼中展示了Banner的開(kāi)啟及打印位置的設(shè)置。程序通過(guò)Banner.Mode枚舉值來(lái)判斷是否開(kāi)啟Banner打印,此項(xiàng)參數(shù)可以在SpringBoot入口nmain方法中通過(guò)setBannerMode方法來(lái)設(shè)置,也可以通過(guò)application.properties中的spring.main.banner-mode進(jìn)行設(shè)置。
SpringApplicationBannerPrinter類承載了Banner初始化及打印的核心功能,比如默認(rèn)如何獲取Banner信息、如何根據(jù)約定優(yōu)于配置來(lái)默認(rèn)獲得Banner的內(nèi)容、Banner支持的文件格式等。
而具體打印的信息是由Banner接口的實(shí)現(xiàn)類來(lái)完成的,比如默認(rèn)情況下使用SpringBootBanner來(lái)打印SpringBoot的版本信息及簡(jiǎn)單的圖形。當(dāng)然還有通過(guò)資源文件打印的ResourceBanner,通過(guò)圖片打印的ImageBanner等方法。
Spring應(yīng)用上下文的創(chuàng)建
SpringBoot創(chuàng)建Spring的應(yīng)用上下文時(shí),如果未指定要?jiǎng)?chuàng)建的類,則會(huì)根據(jù)之前推斷出的類型來(lái)進(jìn)行默認(rèn)上下文類的創(chuàng)建。
在SpringBoot中通過(guò)SpringApplication類中的createApplicationContext來(lái)進(jìn)行應(yīng)用上下文的創(chuàng)建,代碼如下:
protected ConfigurableApplicationContext createApplicationContext() {// 獲取容器的類變量Class<?> contextClass = this.applicationContextClass;// 如果為null,則根據(jù)Web應(yīng)用類型按照默認(rèn)類進(jìn)行創(chuàng)建if (contextClass == null) {try {switch(this.webApplicationType) {case SERVLET:contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");break;case REACTIVE:contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");break;default:contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");}} catch (ClassNotFoundException var3) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);}}return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
Spring應(yīng)用上下文的準(zhǔn)備
我們?cè)谏弦还?jié)完成了應(yīng)用上下文的創(chuàng)建工作,SpringApplication繼續(xù)通過(guò)prepareContext方法來(lái)進(jìn)行應(yīng)用上下文的準(zhǔn)備工作。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 設(shè)置應(yīng)用上下文環(huán)境context.setEnvironment(environment);// 設(shè)置應(yīng)用上下文后置處理this.postProcessApplicationContext(context);// 初始化contextthis.applyInitializers(context);// 通知監(jiān)聽(tīng)器上下文準(zhǔn)備階段listeners.contextPrepared(context);// 打印 profileif (this.logStartupInfo) {this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}// 獲取 ConfigurableListableBeanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// 獲取全部配置源Set<Object> sources = this.getAllSources();Assert.notEmpty(sources, "Sources must not be empty");// 將獲取到的配置源加載到context中this.load(context, sources.toArray(new Object[0]));// 通知監(jiān)聽(tīng)器 context 加載完成listeners.contextLoaded(context);
}
Spring應(yīng)用上下文的刷新
Spring應(yīng)用上下文的刷新,是通過(guò)調(diào)用SpringApplication中的refreshContext方法來(lái)完成的。SpringApplication中refreshContext方法相關(guān)代碼如下:
private void refreshContext(ConfigurableApplicationContext context) {this.refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();} catch (AccessControlException var3) {}}}protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext)applicationContext).refresh();
}
其中refresh方法調(diào)用的是AbstractApplicationContext中的refresh方法,該類屬于spring-context包。AbstractApplicationContext的refresh方法更多的是Spring相關(guān)的內(nèi)容。
public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}
}
在上面的代碼中,調(diào)用finishRefresh方法初始化容器的生命周期處理器并發(fā)布容器的生命周期事件之后,Spring應(yīng)用上下文正式開(kāi)啟,SpringBoot核心特性也隨之啟動(dòng)。完成refreshContext方法操作之后,調(diào)用afterRefresh方法。
完成以上操作之后,調(diào)用SpringApplicationRunListeners的started方法,通知監(jiān)聽(tīng)器容器啟動(dòng)完成,并調(diào)用ApplicationRunner和CommandLineRunner的運(yùn)行方法。
調(diào)用ApplicationRunner和CommandLineRunner
ApplicationRunner和CommandLineRunner是通過(guò) SpringApplication類的callRunners方法來(lái)完成的,具體代碼如下:
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);Iterator var4 = (new LinkedHashSet(runners)).iterator();while(var4.hasNext()) {Object runner = var4.next();if (runner instanceof ApplicationRunner) {this.callRunner((ApplicationRunner)runner, args);}if (runner instanceof CommandLineRunner) {this.callRunner((CommandLineRunner)runner, args);}}}
以上代碼,首先從context中獲得類型為ApplicationRunner和CommandLineRunner的Bean,將它們放入List列表中并進(jìn)行排序。然后再遍歷調(diào)用其run方法,并將ApplicationArguments參數(shù)傳入。
SpringBoot提供這兩個(gè)接口的目的,是為了我們?cè)陂_(kāi)發(fā)的過(guò)程中,通過(guò)它們來(lái)實(shí)現(xiàn)在容器啟動(dòng)時(shí)執(zhí)行一些操作,如果有多個(gè)實(shí)現(xiàn)類,可通過(guò)@Order注解或?qū)崿F(xiàn)Ordered接口來(lái)控制執(zhí)行順序。
這兩個(gè)接口都提供了一個(gè)run方法,但不同之處在于:ApplicationRunner中run方法的參數(shù)為ApplicationArguments,而CommandLineRunner接口中run方法的參數(shù)為String數(shù)組。
以上方法執(zhí)行完成后,會(huì)通過(guò)SpringApplicationRunListeners的running方法通知監(jiān)聽(tīng)器:容器此刻已處于運(yùn)行狀態(tài)。至此,SpringApplication的run方法執(zhí)行完畢。