Springboot Code的啟動(dòng)源碼是怎樣的,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
創(chuàng)新互聯(lián)是專業(yè)的廣水網(wǎng)站建設(shè)公司,廣水接單;提供網(wǎng)站制作、成都做網(wǎng)站,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行廣水網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
項(xiàng)目啟動(dòng)的流程:
(一)new SpringApplication
配置source和web環(huán)境;
創(chuàng)建初始化構(gòu)造器和應(yīng)用監(jiān)聽器;
配置應(yīng)用的主方法所在類;
(二)run 第一二部分
1.獲取并啟動(dòng)監(jiān)聽器
初始化計(jì)時(shí)stopWatch、啟動(dòng)上下文bootstrapContext、設(shè)置系統(tǒng)參數(shù)headless;
初始化監(jiān)聽器列表SpringApplicationRunListeners;
發(fā)布springboot開始啟動(dòng)事件(從applicationListeners中過濾出4個(gè)能監(jiān)聽ApplicationStartingEvent事件的,并啟動(dòng)它們)
2.準(zhǔn)備環(huán)境
裝配命令行參數(shù)applicationArguments(對(duì)象中裝載4個(gè)propertySource);
準(zhǔn)備應(yīng)用程序運(yùn)行的環(huán)境ConfigurableEnvironment(從applicationListeners中過濾出6個(gè)能監(jiān)聽ApplicationEnvironmentPreparedEvent事件的,并啟動(dòng)它們。監(jiān)聽器中關(guān)聯(lián)啟動(dòng)了一些后置處理器處理數(shù)據(jù),最終目的是為應(yīng)用環(huán)境做準(zhǔn)備)
3.打印banner
public ConfigurableApplicationContext run(String... args) { //1、StopWatch簡(jiǎn)單的看成一個(gè)stop watch的機(jī)制,保存stop的記錄信息。 //初始化一個(gè)計(jì)時(shí)器,并開始計(jì)時(shí) StopWatch stopWatch = new StopWatch(); stopWatch.start(); //初始化啟動(dòng)上下文 DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); ConfigurableApplicationContext context = null; //2、configureHeadlessProperty即配置headless模式,這種模式是一種系統(tǒng)缺少顯示設(shè)備、鍵盤和鼠標(biāo)外設(shè)的情況模式。 this.configureHeadlessProperty(); //3、SpringApplicationListeners為SpringApplicationRunListener接口實(shí)現(xiàn)集合(創(chuàng)建SpringApplicationRunListener初始化構(gòu)造器)初始化監(jiān)聽器列表 //可以理解這個(gè)接口就是在spring啟動(dòng)整個(gè)過程都需要回調(diào)這些listener //debug能發(fā)現(xiàn),拿到了一個(gè)名為EventPublishingRunListener(RunListener構(gòu)造方法中關(guān)聯(lián)上了全部applicationListener),這個(gè)就是用來進(jìn)行觸發(fā)publishEvent的被觀察者 SpringApplicationRunListeners listeners = this.getRunListeners(args); //啟動(dòng)EventPublishingRunListener,從而過濾并啟動(dòng)相關(guān)Listener listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //4、ConfigurableEnvironment為配置環(huán)境對(duì)象,簡(jiǎn)單理解所有的配置信息匯總在這個(gè)對(duì)象中 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); //5、Banner就是我們常在控制臺(tái)輸出的畫面橫幅,可以使用圖片或者文本進(jìn)行替換 Banner printedBanner = this.printBanner(environment); =====> //6、ConfigurableApplicationContext根據(jù)webApp…Type進(jìn)行構(gòu)造的上下文對(duì)象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //7、接下來進(jìn)入關(guān)鍵步驟的第一步:prepareContext,準(zhǔn)備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準(zhǔn)備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //8、refreshContext,可以理解成容器初始化節(jié)點(diǎn),將執(zhí)行bean的創(chuàng)建和實(shí)例化。 this.refreshContext(context); //9、afterRefresh,容器后處理, 可以看到會(huì)找到ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個(gè)方法是個(gè)空方法,applicationRun和commandRun移到啟動(dòng)最后。 this.afterRefresh(context, applicationArguments); //10、然后根據(jù)stopwatch打印出啟動(dòng)時(shí)間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類 this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
下面開始對(duì)context的創(chuàng)建與準(zhǔn)備
context = this.createApplicationContext(); protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); }
public interface ApplicationContextFactory { ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch(webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } } catch (Exception var2) { throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2); } }; ConfigurableApplicationContext create(WebApplicationType webApplicationType); .... }
上述代碼可以通過webApplicationTyep(即SERVLET)創(chuàng)建 AnnotationConfigServletWebServerApplicationContext 對(duì)象,對(duì)象創(chuàng)建過程中,初始化了父類的屬性值,其中有三個(gè)比較關(guān)鍵的值,reader、scanner和父類中的beanFactory,下面這個(gè)對(duì)象的層級(jí)結(jié)構(gòu)需要了解一下
/** * reader 和 scanner 都是在構(gòu)造方法中進(jìn)行了賦值 **/ public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set> annotatedClasses; private String[] basePackages; public AnnotationConfigServletWebServerApplicationContext() { this.annotatedClasses = new LinkedHashSet(); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } ... }
我們可以在 GenericApplicationContext 類的構(gòu)造方法中看到其構(gòu)造方法中,對(duì)beanFactory屬性的賦值
public GenericApplicationContext() { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); this.beanFactory = new DefaultListableBeanFactory(); }
//6、ConfigurableApplicationContext根據(jù)webApp…Type進(jìn)行構(gòu)造的上下文對(duì)象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); =====> //7、接下來進(jìn)入關(guān)鍵步驟的第一步:prepareContext,準(zhǔn)備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準(zhǔn)備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //8、refreshContext,可以理解成容器初始化節(jié)點(diǎn),將執(zhí)行bean的創(chuàng)建和實(shí)例化。 this.refreshContext(context); //9、afterRefresh,容器后處理, 可以看到會(huì)找到ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個(gè)方法是個(gè)空方法,applicationRun和commandRun移到啟動(dòng)最后。 this.afterRefresh(context, applicationArguments); //10、然后根據(jù)stopwatch打印出啟動(dòng)時(shí)間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類 this.callRunners(context, applicationArguments);
/** * Spring容器準(zhǔn)備 */ private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 設(shè)置上下文環(huán)境 context.setEnvironment(environment); // postProcessApplicationContext(context); // 執(zhí)行所有ApplicationContextInitializer對(duì)象的initialize方法(這些對(duì)象是通過讀取spring.factories加載) applyInitializers(context); // 發(fā)布上下文準(zhǔn)備完成事件到所有監(jiān)聽器 listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set
debug,此處load方法可以跟進(jìn)多層到 AnnotatedBeanDefinitionReader 的 registerBean(啟動(dòng)類)
public void registerBean(Class> beanClass) { this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null); }
這里同樣有兩個(gè)listeners.*的兩個(gè)方法,和前面listeners.starting()是一樣的,提供下最近整理的類圖(了解Runlistener和listener類之間的關(guān)系)
//6、ConfigurableApplicationContext根據(jù)webApp…Type進(jìn)行構(gòu)造的上下文對(duì)象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //7、接下來進(jìn)入關(guān)鍵步驟的第一步:prepareContext,準(zhǔn)備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準(zhǔn)備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); =====> //8、refreshContext,可以理解成容器初始化節(jié)點(diǎn),將執(zhí)行bean的創(chuàng)建和實(shí)例化。 this.refreshContext(context); //9、afterRefresh,容器后處理, 可以看到會(huì)找到ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個(gè)方法是個(gè)空方法,applicationRun和commandRun移到啟動(dòng)最后。 this.afterRefresh(context, applicationArguments); //10、然后根據(jù)stopwatch打印出啟動(dòng)時(shí)間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類 this.callRunners(context, applicationArguments);
refreshContext() 是整個(gè)Run方法的核心部分
/** * 刷新應(yīng)用程序上下文 * * @param context */ private void refreshContext(ConfigurableApplicationContext context) { // 注冊(cè)一個(gè)關(guān)閉鉤子,在jvm停止時(shí)會(huì)觸發(fā),然后退出時(shí)執(zhí)行一定的退出邏輯 if (this.registerShutdownHook) { try { // 添加:Runtime.getRuntime().addShutdownHook() // 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook) context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } // ApplicationContext真正開始初始化容器和創(chuàng)建bean的階段 refresh((ApplicationContext) context); } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); refresh((ConfigurableApplicationContext) applicationContext); } protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); }
調(diào)用應(yīng)用上下文對(duì)象的refresh()方法,接下來我i門到ConfigurableApplicationContext類中去看下這個(gè)方法
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { void refresh() throws BeansException, IllegalStateException; }
這是一個(gè)接口,且這個(gè)類是在spring框架中,非springboot,它的實(shí)現(xiàn)類共有三個(gè)
AbstractApplicationContext是一個(gè)抽象類,其余兩個(gè)類都繼承了它,我們來看看這個(gè)抽象類的代碼:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 第一步:準(zhǔn)備更新上下時(shí)的預(yù)備工作 prepareRefresh(); // 第二步:獲取上下文內(nèi)部BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 第三步:對(duì)BeanFactory做預(yù)備工作 prepareBeanFactory(beanFactory); try { // 第四步:允許在上下文子類中對(duì)bean工廠進(jìn)行post-processing postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // 第五步:調(diào)用上下文中注冊(cè)為bean的工廠 BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 第六步:注冊(cè)攔截bean創(chuàng)建的攔截器 registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // 第七步:初始化MessageSource(國(guó)際化相關(guān)) initMessageSource(); // 第八步:初始化容器事件廣播器(用來發(fā)布事件) initApplicationEventMulticaster(); // 第九步:初始化一些特殊的bean onRefresh(); // 第十步:將所有監(jiān)聽器注冊(cè)到前兩步創(chuàng)建的事件廣播器中 registerListeners(); // 第十一步:結(jié)束bean的初始化工作(主要將所有單例BeanDefinition實(shí)例化) finishBeanFactoryInitialization(beanFactory); // 第十二步:afterRefresh(上下文刷新完畢,發(fā)布相應(yīng)事件) finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); contextRefresh.end(); } } }
這里有非常多的步驟,上下文對(duì)象主要的bean也是在這里進(jìn)行處理的,具體的說明可以看注釋, fresh方法就是SpringFrameWork的那部分(不再細(xì)化)
//6、ConfigurableApplicationContext根據(jù)webApp…Type進(jìn)行構(gòu)造的上下文對(duì)象 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //7、接下來進(jìn)入關(guān)鍵步驟的第一步:prepareContext,準(zhǔn)備容器階段,將執(zhí)行所有的initializers邏輯,做初始化準(zhǔn)備操作。 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //8、refreshContext,可以理解成容器初始化節(jié)點(diǎn),將執(zhí)行bean的創(chuàng)建和實(shí)例化。 this.refreshContext(context); =====> //9、afterRefresh,容器后處理, 可以看到會(huì)找到ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類并執(zhí)行。但從2.x版本來看,似乎這個(gè)方法是個(gè)空方法,applicationRun和commandRun移到啟動(dòng)最后。 this.afterRefresh(context, applicationArguments); =====> //10、然后根據(jù)stopwatch打印出啟動(dòng)時(shí)間 stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); //11、這里調(diào)用ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類 this.callRunners(context, applicationArguments);
afterRefresh() 是對(duì)方法 refresh() 的擴(kuò)展,暫時(shí)空方法。
stopWatch.stop() 根據(jù)stopwatch打印出啟動(dòng)時(shí)間,至此項(xiàng)目已經(jīng)啟動(dòng)完成。
* run方法主要做如下幾件事情:
發(fā)出啟動(dòng)結(jié)束事件
執(zhí)行實(shí)現(xiàn)ApplicationRunner、CommandLineRunner的run方法
發(fā)布應(yīng)用程序已啟動(dòng)(ApplicationStartedEvent)事件
結(jié)合網(wǎng)上共享的兩張圖可以清晰回顧下整體流程:
關(guān)于Springboot Code的啟動(dòng)源碼是怎樣的問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。