SpringBoot的啟動--源碼學習

啟動

spring boot啟動代碼如下:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

代碼顯示,main方法中執行SpringApplication的靜態方法run(),run方法中會構造一個SpringApplication實例,然后執行。

SpringApplication的構造過程

首先SpringApplication會執行構造函數:

    public SpringApplication(Object... sources) {
        initialize(sources);
    }

debug會發現source值為:


sources.png

sources目前是一個Application的class對象
構造函數中會執行initialize()方法:

image.png

debug類deduceWebEnvironment
這個方法中,首先deduceWebEnvironment檢驗網絡環境。具體方法是檢查默認類加載器是否加載過Servlet和ConfigurableWebApplicationContext這兩個類。如果加載過,那么即為Web應用。

image.png

image.png

initialize方法中:
spring.factories文件中找出key為ApplicationContextInitializer的類并實例化后設置到SpringApplicationinitializers屬性中。這個過程也就是找出所有的應用程序初始化器;第二步,getSpringFactoriesInstances方法接受ApplicationContextInitializer作為參數。然后一直調用到getSpringFactoriesInstances方法。

      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

此時ApplicationContextInitializer接口,應用程序初始化器,做一些初始化的工作:

ApplicationContextInitializer.png

接口實現如下圖:


image.png
分析getSpringFactoriesInstances
getSpringFactoriesInstances.png

上面的SpringFactoriesLoader.loadFactoryNames方法看這里

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

loadFactoryNames.png

可以查看下spring.factories文件,spring-boot-autoconfigure和 spring-boot的jar包中都有
image.png

當SpringApplication創建,初始化了上述的 Application ContextApplication Listeners

image

通過spring.factories文件拿到一系列的Context和Listener之后 執行run方法
run方法會從spring.factories文件中獲取到run listener,然后在spirng boot 執行到各個階段時執行Listener事件和Context事件
所以,所謂的SpringApplicationRunListeners實際上就是在SpringApplication對象的run方法執行的不同階段,去執行一些操作,并且這些操作是可配置的。

SpringApplication的run方法代碼如下:
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch(); // 構造一個任務執行觀察器
        stopWatch.start(); // 開始執行,記錄開始時間
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        // 獲取SpringApplicationRunListeners,內部只有一個EventPublishingRunListener
        SpringApplicationRunListeners listeners = getRunListeners(args);
         // 上面分析過,會封裝成SpringApplicationEvent事件然后廣播出去給SpringApplication中的listeners所監聽
        // 這里接受ApplicationStartedEvent事件的listener會執行相應的操作
        listeners.started();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args); // 構造一個應用程序參數持有類
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments); //應用程序的環境信息準備
            Banner printedBanner = printBanner(environment); // 是否在控制臺上打印自定義的banner
            context = createApplicationContext();  // 創建Spring容器
            analyzers = new FailureAnalyzers(context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);  //準備容器
            refreshContext(context);
            afterRefresh(context, applicationArguments);   // 容器創建完成之后執行額外一些操作
            listeners.finished(context, null);// 廣播出ApplicationReadyEvent事件給相應的監聽器執行
            stopWatch.stop(); // 執行結束,記錄執行時間
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context; // 返回Spring容器
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

下面看其中的部分方法:

prepareEnvironment
    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        // 創建應用程序的環境信息。如果是web程序,創建StandardServletEnvironment;否則,創建StandardEnvironment
        ConfigurableEnvironment environment = getOrCreateEnvironment(); 
          // 配置一些環境信息。比如profile,命令行參數
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        listeners.environmentPrepared(environment);// 廣播出ApplicationEnvironmentPreparedEvent事件給相應的監聽器執行
        if (isWebEnvironment(environment) && !this.webEnvironment) {   // 環境信息的校對
            environment = convertToStandardEnvironment(environment);
        }
        return environment;
    }
Spring容器的創建createApplicationContext方法如下:
protected ConfigurableApplicationContext createApplicationContext() {
      Class<?> contextClass = this.applicationContextClass;
      if (contextClass == null) {
        try {
          // 如果是web程序,那么構造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器
          // 否則構造org.springframework.context.annotation.AnnotationConfigApplicationContext容器
          contextClass = Class.forName(this.webEnvironment
              ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
        }
        catch (ClassNotFoundException ex) {
          throw new IllegalStateException(
              "Unable create a default ApplicationContext, "
                  + "please specify an ApplicationContextClass",
              ex);
        }
      }
      return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }

prepareContext 準備容器的方法
private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment); // 設置Spring容器的環境信息
        postProcessApplicationContext(context); // 回調方法,Spring容器創建之后做一些額外的事
        applyInitializers(context); // SpringApplication的的初始化器開始工作
        listeners.contextPrepared(context); // 遍歷調用SpringApplicationRunListener的contextPrepared方法。目前只是將這個事件廣播器注冊到Spring容器中
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // Add boot specific singleton beans  
        // 把應用程序參數持有類注冊到Spring容器中,并且是一個單例
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        // Load the sources  
        Set<Object> sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);  // 廣播出ApplicationPreparedEvent事件給相應的監聽器執行
    }
refreshContext 刷新容器:

Spring容器的刷新refresh方法內部會做很多很多的事情:比如BeanFactory的設置,BeanFactoryPostProcessor接口的執行、BeanPostProcessor接口的執行、自動化配置類的解析、條件注解的解析、國際化的初始化等等。

    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);        // Spring容器的刷新
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
afterRefresh

run方法中的Spring容器創建完成之后會調用afterRefresh方法,代碼如下:

    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
        callRunners(context, args);/ 調用Spring容器中的ApplicationRunner和CommandLineRunner接口的實現類
    }

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 找出Spring容器中ApplicationRunner接口的實現類
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());// 找出Spring容器中CommandLineRunner接口的實現類
        AnnotationAwareOrderComparator.sort(runners); // 對runners進行排序
        for (Object runner : new LinkedHashSet<Object>(runners)) { // 遍歷runners依次執行
            if (runner instanceof ApplicationRunner) {  // 如果是ApplicationRunner,進行ApplicationRunner的run方法調用
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) { // 如果是CommandLineRunner,進行CommandLineRunner的run方法調用
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

這樣run方法執行完成之后。Spring容器也已經初始化完成,各種監聽器和初始化器也做了相應的工作。

總結

SpringBoot啟動的時候,不論調用什么方法,都會構造一個SpringApplication的實例,然后調用這個實例的run方法,這樣就表示啟動SpringBoot。

在run方法調用之前,也就是構造SpringApplication的時候會進行初始化的工作,初始化的時候會做以下幾件事:
1 把參數sources設置到SpringApplication屬性中,這個sources可以是任何類型的參數。本文的例子中這個sources就是Application的class對象
2 判斷是否是web程序,并設置到webEnvironment這個boolean屬性中
3 找出所有的初始化器,默認有5個,設置到initializers屬性中
4 找出所有的應用程序監聽器,默認有9個,設置到listeners屬性中
5 找出運行的主類(main class)
SpringApplication構造完成之后調用run方法,啟動SpringApplication,run方法執行的時候會做以下幾件事:
1 構造一個StopWatch,觀察SpringApplication的執行
2 找出所有的SpringApplicationRunListener并封裝到SpringApplicationRunListeners中,用于監聽run方法的執行。監聽的過程中會封裝成事件并廣播出去讓初始化過程中產生的應用程序監聽器進行監聽
3 構造Spring容器(ApplicationContext),并返回
     3.1 創建Spring容器的判斷是否是web環境,是的話構造
       AnnotationConfigEmbeddedWebApplicationContext,否則構造
       AnnotationConfigApplicationContext
     3.2 初始化過程中產生的初始化器在這個時候開始工作
     3.3 Spring容器的刷新(完成bean的解析、各種processor接口的執行、條件注解的解析等等)
4 從Spring容器中找出ApplicationRunner和CommandLineRunner接口的實現類并排序后依次執行
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容