SpringBoot啟動流程分析(一):SpringApplication類初始化過程
SpringBoot系列文章簡介
SpringBoot源碼閱讀輔助篇:
Spring IoC容器與應用上下文的設計與實現
SpringBoot啟動流程源碼分析:
- SpringBoot啟動流程分析(一):SpringApplication類初始化過程
- SpringBoot啟動流程分析(二):SpringApplication的run方法
- SpringBoot啟動流程分析(三):SpringApplication的run方法之prepareContext()方法
- SpringBoot啟動流程分析(四):IoC容器的初始化過程
- SpringBoot啟動流程分析(五):SpringBoot自動裝配原理實現
- SpringBoot啟動流程分析(六):IoC容器依賴注入
筆者註釋版Spring Framework與SpringBoot源碼git傳送門:請不要吝嗇小星星
- spring-framework-5.0.8.RELEASE
- SpringBoot-2.0.4.RELEASE
一、SpringApplication初始化過程
1.1、SpringBoot項目的mian函數
常規的這個主類如下圖所示,我們一般會這樣去寫。
在這個類中需要關注的是
- @SpringBootApplication
- SpringApplication.run()
關於 @SpringBootApplication 註解,在後面分析SpringBoot自動裝配的章節會展開去分析。
本章節中我們需要關注的就是 SpringApplication.run() 方法。
查看run()方法的實現,如下面代碼所示,我們發現其實其首先是創建了 SpringApplication 的實例,然後調用了 SpringApplication 的run()方法,那本章我們關注的就是 SpringApplication 創建實例的過程。
/** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * * @param primarySources the primary sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
1.2、 SpringApplication() 構造方法
繼續查看源碼, SpringApplication 實例化過程,首先是進入但參數的構造方法,最終回來到兩個參數的構造方法。
1 public SpringApplication(Class<?>... primarySources) { 2 this(null, primarySources); 3 } 4 5 @SuppressWarnings({"unchecked", "rawtypes"}) 6 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 7 this.resourceLoader = resourceLoader; 8 Assert.notNull(primarySources, "PrimarySources must not be null"); 9 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 10 //推斷應用類型,後面會根據類型初始化對應的環境。常用的一般都是servlet環境 11 this.webApplicationType = deduceWebApplicationType();//2.2.1 12 //初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer 13 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//2.2.2 14 //初始化classpath下所有已配置的 ApplicationListener 15 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//2.2.3 16 //根據調用棧,推斷出 main 方法的類名 17 this.mainApplicationClass = deduceMainApplicationClass(); 18 }
1.2.1、deduceWebApplicationType();該方法推斷應用的類型。 SERVLET REACTIVE NONE
1 //常量值 2 private static final String[] WEB_ENVIRONMENT_CLASSES = {"javax.servlet.Servlet", 3 "org.springframework.web.context.ConfigurableWebApplicationContext"}; 4 5 private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework." 6 + "web.reactive.DispatcherHandler"; 7 8 private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework." 9 + "web.servlet.DispatcherServlet"; 10 11 private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig"; 12 13 /** 14 * 判斷 應用的類型 15 * NONE: 應用程序不是web應用,也不應該用web服務器去啟動 16 * SERVLET: 應用程序應作為基於servlet的web應用程序運行,並應啟動嵌入式servlet web(tomcat)服務器。 17 * REACTIVE: 應用程序應作為 reactive web應用程序運行,並應啟動嵌入式 reactive web服務器。 18 * @return 19 */ 20 private WebApplicationType deduceWebApplicationType() { 21 //classpath下必須存在org.springframework.web.reactive.DispatcherHandler 22 if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) 23 && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null) 24 && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) { 25 return WebApplicationType.REACTIVE; 26 } 27 for (String className : WEB_ENVIRONMENT_CLASSES) { 28 if (!ClassUtils.isPresent(className, null)) { 29 return WebApplicationType.NONE; 30 } 31 } 32 //classpath環境下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext 33 return WebApplicationType.SERVLET; 34 }
返回類型是WebApplicationType的枚舉類型, WebApplicationType 有三個枚舉,三個枚舉的解釋如其中註釋
具體的判斷邏輯如下:
-
WebApplicationType.REACTIVE classpath下存在org.springframework.web.reactive.DispatcherHandler
-
WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext
-
WebApplicationType.NONE 不滿足以上條件。
1.2.2、 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer。
1 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { 2 return getSpringFactoriesInstances(type, new Class<?>[]{}); 3 } 4 5 /** 6 * 通過指定的classloader 從META-INF/spring.factories獲取指定的Spring的工廠實例 7 * @param type 8 * @param parameterTypes 9 * @param args 10 * @param <T> 11 * @return 12 */ 13 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, 14 Class<?>[] parameterTypes, Object... args) { 15 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 16 // Use names and ensure unique to protect against duplicates 17 //通過指定的classLoader從 META-INF/spring.factories 的資源文件中, 18 //讀取 key 為 type.getName() 的 value 19 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 20 //創建Spring工廠實例 21 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, 22 classLoader, args, names); 23 //對Spring工廠實例排序(org.springframework.core.annotation.Order註解指定的順序) 24 AnnotationAwareOrderComparator.sort(instances); 25 return instances; 26 }
看看 getSpringFactoriesInstances 都幹了什麼,看源碼,有一個方法很重要 loadFactoryNames() 這個方法很重要,這個方法是spring-core中提供的從META-INF/spring.factories中獲取指定的類(key)的同一入口方法。
在這裏,獲取的是key為 org.springframework.context.ApplicationContextInitializer 的類。
debug看看都獲取到了哪些
上面說了,是從classpath下 META-INF/spring.factories中獲取,我們驗證一下:
發現在上圖所示的兩個工程中找到了debug中看到的6條結果。 ApplicationContextInitializer 是Spring框架的類, 這個類的主要目的就是在 ConfigurableApplicationContext 調用refresh()方法之前,回調這個類的initialize方法。通過 ConfigurableApplicationContext 的實例獲取容器的環境Environment,從而實現對配置文件的修改完善等工作。
關於怎麼實現自定義的 ApplicationContextInitializer 請看我的另一篇專門介紹該類的博客。
1.2.3、 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
初始化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。
ApplicationListener 的加載過程和上面的 ApplicationContextInitializer 類的加載過程是一樣的。不多說了,至於 ApplicationListener 是spring的事件監聽器,典型的觀察者模式,通過 ApplicationEvent 類和 ApplicationListener 接口,可以實現對spring容器全生命周期的監聽,當然也可以自定義監聽事件。為了梳理springboot的啟動流程在這裏先不說這個了。後面有時間的話再介紹。
關於ApplicationContextInitializer的詳細介紹請看<SpringBoot之ApplicationContextInitializer的理解和使用>
二、總結
關於 SpringApplication 類的構造過程,到這裏我們就梳理完了。縱觀 SpringApplication 類的實例化過程,我們可以看到,合理的利用該類,我們能在spring容器創建之前做一些預備工作,和定製化的需求。
比如,自定義SpringBoot的Banner,比如自定義事件監聽器,再比如在容器refresh之前通過自定義 ApplicationContextInitializer 修改配置一些配置或者獲取指定的bean都是可以的。。。
下一節開始分析SpringBoot容器的構建過程,也就是那個大家多少都看過的run();方法。
原創不易,轉載請註明出處。
如有錯誤的地方還請留言指正。
【精選推薦文章】
智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選
想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計
廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益