<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
每個Spring Boot專案都有一個主程式啟動類,在主程式啟動類中有一個啟動專案的main()方法,在該方法中通過執行SpringApplication.run()即可啟動整個Spring Boot程式。
問題:那麼SpringApplication.run()方法到底是如何做到啟動Spring Boot專案的呢?
下面我們檢視run()方法內部的原始碼,核心程式碼具體如下:
@SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootDemoApplication.class, args); } }
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
從上述原始碼可以看出,SpringApplication.run()方法內部執行了兩個操作,分別是SpringApplication範例的初始化建立和呼叫run()啟動專案,這兩個階段的實現具體說明如下
檢視SpringApplication範例物件初始化建立的原始碼資訊,核心程式碼具體如下
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 把專案啟動類.class設定為屬性儲存起來 this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); // 判斷當前webApplicationType應用的型別 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 設定初始化器(Initializer),最後會呼叫這些初始化器 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 設定監聽器(Listener) this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // 用於推斷並設定專案main()方法啟動的主程式啟動類 this.mainApplicationClass = this.deduceMainApplicationClass();
從上述原始碼可以看出,SpringApplication的初始化過程主要包括4部分,具體說明如下。
(1)this.webApplicationType = WebApplicationType.deduceFromClasspath()
用於判斷當前webApplicationType應用的型別。deduceFromClasspath()方法用於檢視Classpath類路徑下是否存在某個特徵類,從而判斷當前webApplicationType型別是SERVLET應用(Spring 5之前的傳統MVC應用)還是REACTIVE應用(Spring 5開始出現的WebFlux互動式應用)
(2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
用於SpringApplication應用的初始化器設定。在初始化器設定過程中,會使用Spring類載入器SpringFactoriesLoader從META-INF/spring.factories類路徑下的META-INF下的spring.factores檔案中獲取所有可用的應用初始化器類ApplicationContextInitializer。
(3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
用於SpringApplication應用的監聽器設定。監聽器設定的過程與上一步初始化器設定的過程基本一樣,也是使用SpringFactoriesLoader從META-INF/spring.factories類路徑下的META-INF下的spring.factores檔案中獲取所有可用的監聽器類ApplicationListener。
(4)this.mainApplicationClass = this.deduceMainApplicationClass()
用於推斷並設定專案main()方法啟動的主程式啟動類
分析完(new SpringApplication(primarySources)).run(args)原始碼前一部分SpringApplication範例物件的初始化建立後,檢視run(args)方法執行的專案初始化啟動過程,核心程式碼具體如下:
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); // 第二步:根據SpringApplicationRunListeners以及引數來準備環境 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); // 準備Banner列印器 - 就是啟動Spring Boot的時候列印在console上的ASCII藝術字型 Banner printedBanner = this.printBanner(environment); // 第三步:建立Spring容器 context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context}); // 第四步:Spring容器前置處理 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 第五步:重新整理容器 this.refreshContext(context); // 第六步:Spring容器後置處理 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); } }
從上述原始碼可以看出,專案初始化啟動過程大致包括以下部分:
第一步:獲取並啟動監聽器
this.getRunListeners(args)和listeners.starting()方法主要用於獲取SpringApplication範例初始化過程中初始化的SpringApplicationRunListener監聽器並執行。
第二步:根據SpringApplicationRunListeners以及引數來準備環境
this.prepareEnvironment(listeners, applicationArguments)方法主要用於對專案執行環境進行預設定,同時通過this.configureIgnoreBeanInfo(environment)方法排除一些不需要的執行環境
第三步:建立Spring容器
根據webApplicationType進行判斷, 確定容器型別,如果該型別為SERVLET型別,會通過反射裝載對應的位元組碼,也就是AnnotationConfigServletWebServerApplicationContext,接著使用之前初始化設定的context(應用上下文環境)、environment(專案執行環境)、listeners(執行監聽器)、applicationArguments(專案引數)和printedBanner(專案圖示資訊)進行應用上下文的組裝設定,並重新整理設定
第四步:Spring容器前置處理
這一步主要是在容器重新整理之前的準備動作。設定容器環境,包括各種變數等等,其中包含一個非常關鍵的操作:將啟動類注入容器,為後續開啟自動化設定奠定基礎
第五步:重新整理容器
開啟重新整理spring容器,通過refresh方法對整個IOC容器的初始化(包括bean資源的定位,解析,註冊等等),同時向JVM執行時註冊一個關機勾點,在JVM關機時會關閉這個上下文,除非當時它已經關閉
第六步:Spring容器後置處理
擴充套件介面,設計模式中的模板方法,預設為空實現。如果有自定義需求,可以重寫該方法。比如列印一些啟動結束log,或者一些其它後置處理。
第七步:發出結束執行的事件
獲取EventPublishingRunListener監聽器,並執行其started方法,並且將建立的Spring容器傳進去了,建立一個ApplicationStartedEvent事件,並執行ConfigurableApplicationContext 的
publishEvent方法,也就是說這裡是在Spring容器中釋出事件,並不是在SpringApplication中釋出事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的監聽器釋出啟動事件。
第八步:執行Runners
用於呼叫專案中自定義的執行器XxxRunner類,使得在專案啟動完成後立即執行一些特定程式。其中,Spring Boot提供的執行器介面有ApplicationRunner 和CommandLineRunner兩種,在使用時只需要自定義一個執行器類實現其中一個介面並重寫對應的run()方法介面,然後Spring Boot專案啟動後會立即執行這些特定程式
下面,通過一個Spring Boot執行流程圖,讓大家更清晰的知道Spring Boot的整體執行流程和主要啟動階段:
到此這篇關於SpringBoot詳解執行過程的文章就介紹到這了,更多相關SpringBoot執行過程內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45