SpringBoot系列文章簡介
SpringBoot原始碼閱讀輔助篇:
SpringBoot啟動流程原始碼分析:
- SpringBoot啟動流程分析(一):SpringApplication類初始化過程
- SpringBoot啟動流程分析(二):SpringApplication的run方法
- SpringBoot啟動流程分析(三):SpringApplication的run方法之prepareContext()方法
- SpringBoot啟動流程分析(四):IoC容器的初始化過程
- SpringBoot啟動流程分析(五):SpringBoot自動裝配原理實現
- SpringBoot啟動流程分析(六):IoC容器依賴注入
筆者註釋版Spring Framework與SpringBoot原始碼git傳送門:請不要吝嗇小星星
一、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();方法。
原創不易,轉載請註明出處。
如有錯誤的地方還請留言指正。