SpringBoot啓動流程簡析(二)

在這篇文章中,我們接着上一篇的內容接着分析。

    public ConfigurableApplicationContext run(String... args) {
        //啓動應用的檢測
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //SpringBoot的上下文
        ConfigurableApplicationContext context = null;
        //失敗分析報告
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        //SpringBoot的runlistener
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
        //參數解析
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
                    //配置環境變量
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            //輸出Banner信息        
            Banner printedBanner = printBanner(environment);
            //創建應用上下文
            context = createApplicationContext();
            analyzers = new FailureAnalyzers(context);
            //refresh上下文之前的準備
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.finished(context, null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

SpringApplication中的run方法的內容如上所示,上面就是整個SpringBoot應用啓動的主要調用方法,run方法中的參數即是我們的應用參數。下面我們來簡單的分析一下這個啓動過程。
StopWatch主要是監控啓動過程,統計啓動時間,檢測應用是否已經啓動或者停止。

SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
        SpringApplicationRunListener.class, types, this, args));
}

通過在上一篇文章中的問題,對於getSpringFactoriesInstances這個方法你應該不陌生來吧。這裏也是從META-INF/spring.factories中獲取類型爲org.springframework.boot.SpringApplicationRunListener的配置值,這個默認的配置值爲:org.springframework.boot.context.event.EventPublishingRunListener。我們進入到EventPublishingRunListener這個類看一下它的構造函數

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        //創建一個SimpleApplicationEventMulticaster
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //把之前在SpringApplication中獲取到的listener循環放入到SimpleApplicationEventMulticaster中
        for (ApplicationListener<?> listener : application.getListeners()) {
    this.initialMulticaster.addApplicationListener(listener);
        }
    }

通過上面的分析,我們可以看到EventPublishingRunListener把SpringApplication中的監聽器,都放到了SimpleApplicationEventMulticaster中,進行了統一的管理。listeners.starting();啓動事件監聽,這裏以後我們單獨開章節詳細說明.

 //創建應用參數解析器
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);

我們看一下DefaultApplicationArguments的構造函數的內容:

    public DefaultApplicationArguments(String[] args) {
            //首先判斷不能爲null,這裏大家可以想一下可變參數如果不傳值的話看看是什麼內容
        Assert.notNull(args, "Args must not be null");
        //調用Source對應用參數進行解析
        this.source = new Source(args);
        this.args = args;
    }
Source(String[] args) {
//調用父類的構造函數 Source的繼承關係如下圖所示
    super(args);
}
public SimpleCommandLinePropertySource(String... args) {
    //對應參數進行解析的工作
    super(new SimpleCommandLineArgsParser().parse(args));
}

Source
大家在配置應用參數的時候,是這樣這樣配置的 - -key=value,爲什麼要以- -開頭呢?在SimpleCommandLineArgsParser的parse方法中你會找到答案。並且Source這個類還繼承類PropertySource這個類。

//準備環境變量
ConfigurableEnvironment environment =           prepareEnvironment(listeners,applicationArguments); 
private ConfigurableEnvironment prepareEnvironment(
    SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments) {
    //獲取環境變量 
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //將應用參數放入到環境變量持有對象中
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //監聽器監聽環境變量對象的變化
    listeners.environmentPrepared(environment);
    //如果非web環境,則轉換爲StandardEnvironment對象
    if (!this.webEnvironment) {
        environment = new EnvironmentConverter(getClassLoader())            .convertToStandardEnvironmentIfNecessary(environment);
    }
    return environment;
}
    private ConfigurableEnvironment getOrCreateEnvironment() {
        //如果已經創建過存放環境變量的對象了,則直接返回
        if (this.environment != null) {
            return this.environment;
        }
        //如果是web環境則創建StandardServletEnvironment對象
        if (this.webEnvironment) {
            return new StandardServletEnvironment();
        }
        //非web環境,創建StandardEnvironment
        return new StandardEnvironment();
    }

StandardServletEnvironment的UML圖如下所示,StandardServletEnvironment集成了系統變量、環境變量、配置屬性信息等內容。這些內容我們以後單開一個篇章來說一下。
StandardServletEnvironment

//這句話是輸出SpringBoot的Banner信息,可以從指定的位置加載信息,可以輸出爲文字形式,也可以輸出爲圖片形式,如我們常見的SpringBoot的logo就是在這裏輸出的
Banner printedBanner = printBanner(environment);

SpringBootBanner
Banner的UML類圖如下所示:
Banner
我們最常見的SpringBoot的logo“圖像”就在SpringBootBanner這個類中定義的,這個也是SpringBoot默認的Banner類。
SpringBootBanner

//創建SpringBoot的應用上下文
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
        //DEFAULT_WEB_CONTEXT_CLASS = org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
        /// DEFAULT_CONTEXT_CLASS = org.springframework.context.annotation.AnnotationConfigApplicationContext
        contextClass = Class.forName(this.webEnvironment
                ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

因爲我們是web開發環境,所以這裏我們的web上下文是AnnotationConfigEmbeddedWebApplicationContext這個對象,一定要記住這個類,要仔細的看看它的UML類圖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章