springboot啟動流程分析(二)
阿新 • • 發佈:2019-01-22
現在繼續看啟動過程的詳情,詳細描述下SpringApplication建構函式:
1.載入過程中的SpringApplication初始化如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; // 傳入空值初始化
// 判斷是否有入口類
Assert.notNull(primarySources, "PrimarySources must not be null" );
// 轉化為元素插入的順序來維護集合的連結表
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推斷當前環境是什麼環境:Servlet、Reactive、或者是web環境;
this.webApplicationType = deduceWebApplicationType();
// 初始化器initializers:這些初始化器(initializers)是Spring Boot通過讀取每個jar包下的/META-INF/spring.factories檔案中的配置獲取的。每一個initailizer都是一個實現了ApplicationContextInitializer介面的例項。ApplicationContextInitializer是Spring IOC(控制反轉)容器中提供的一個介面:
//【特別要注意的是,這塊的掃描結果,是為了run方法中的prepareContext和refreshContext函式初始化的,參見:https://www.cnblogs.com/hzhuxin/p/7742365.html】
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 監聽器listeners:從spring.factories檔案中找出key為ApplicationListener的類並例項化後設置到SpringApplication的listeners屬性中。這個過程就是找出所有的應用程式事件監聽器
// 【參見:https://blog.csdn.net/Bob_666/article/details/79715156】
setListeners((Collection)
// 該方法獲取執行型別子類例項集合,不太明白????????????????
getSpringFactoriesInstances(ApplicationListener.class));
// 獲取當前呼叫棧,找到入口方法main所在的類,並將其複製給SpringApplication物件的成員變數mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
2.首先來看resourceLoader,傳入空值初始化,什麼是resourceLoader?
ResouceLoader是spring-core中的一個介面類
public interface ResourceLoader {
/** 首先,什麼是classpath:描述了Java虛擬機器在執行一個Class時在哪些路徑中載入要執行的類以及執行的類要用到的類(http://blog.sina.com.cn/s/blog_605f5b4f01010i5u.html)
比如:用intellij idea執行springboot入口main時,控制檯列印的第一條就是帶有-classpath的java執行命令(由於太長,這裡不列出命令詳情),其中classpath引數中的path中,有一條:E:\springdemo\target\classes,這條就是當前springboot應用編譯生成的目錄,即在Maven專案中,所有的resources/src檔案都將被複制到classes目錄下,並且,classpath引數列表的先後順序(也是實際的載入順序,因為classpath會按照從前向後載入,如果有兩個類,那麼在classpath中靠前的會先被載入)為:安裝的JRE類庫、當前應用的target\classes目錄、maven下載的依賴
https://www.javaworld.com/article/2077468/core-java/java-tip-105--mastering-the-classpath-with-jwhich.html */
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/** 通過提供的資源location引數獲取Resource例項,該例項可以是ClasPathResource、FileSystemResource、UrlResource等實現類
資源可以為:
1. URL位置資源,如”file:C:/test.dat”
2. ClassPath位置資源,如”classpath:test.dat”
3.相對路徑資源,如”WEB-INF/test.dat
*/
Resource getResource(String location);
/** 通過getClassLoader獲取ClassLoader例項
http://www.blogjava.net/DLevin/archive/2012/12/01/392337.html
*/
@Nullable
ClassLoader getClassLoader();
關於ResourceLoader的使用,待後面分析
3.Assert判斷確實有入口類傳入
4.將入口類轉化為有向set(是否是為了確保不被亂序載入????,待確認):
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
5.推斷應用框架型別:
/** 判斷:Servlet還是Reactive應用
Servlet:spring原始框架就是為Servlet API和Servlet容器建立的;
Reactive:是spring 5之後支援的一個web應用架構,整合Reactive的是:spring webflux 具體參見:https://springcloud.cc/web-reactive.html
Servlet和Reactive的選擇:https://www.infoq.com/news/2017/12/servlet-reactive-stack
*/
this.webApplicationType = deduceWebApplicationType();
deduceWebApplicationType實現:
/** WebApplicationType 是個列舉值:NONE、SERVLET、REACTIVE */
private WebApplicationType deduceWebApplicationType() {
/** 其中:
REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.reactive.DispatcherHandler";
MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
即判斷:存在REACTIVE的事件處理器,並且不是servlet的事件處理器,則返回REACTIVE
*/
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
/** String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
遍歷該環境CLASS,如果都不存在,那麼就是有問題,即也不是servlet應用
*/
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
/** 最終返回SERVLET型別 */
return WebApplicationType.SERVLET;
}
ClassUtils.isPresent函式:
/** @Nullable表示classLoader可以為空,實際上這裡在呼叫的時候,就是傳入了null,表示使用預設的classloader
即:查詢對應的class是否存在
*/
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
6.初始化器:
這個很重要,是spring初始化的重要過程
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
這裡的 ApplicationContextInitializer 是一個回撥介面類, 是在ConfigurableApplicationContext重新整理之前初始化Spring ConfigurableApplicationContext的回撥介面。當執行:
ConfigurableApplicationContext.refresh()或SpringApplication.run()時生效。
在本例中,也就是run方法中的refresh時候會回撥這些
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
那如何配置ApplicationContextInitializer?有多種方法:
(1)通過繼承實現:
(2)在application.properties中以context.initializer.classes為key配置
(3)通過在spring.factories中配置
比如:https://www.jianshu.com/p/e4a0b900872b
【未完待續】