1. 程式人生 > >springboot啟動流程分析(二)

springboot啟動流程分析(二)

現在繼續看啟動過程的詳情,詳細描述下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
【未完待續】