SpringBoot原理髮現(三)

 

說明:

本系列基於SpringBoot 2.2.9.RELEASE 版本,對SpringBoot的原理進行分析,一共分爲四節:

SpringBoot原理髮現(一):創建Hello World,對pom依賴以及@SpringBootApplication註解進行分析

SpringBoot原理髮現(二):分析SpringBoot自動配置原理

SpringBoot原理髮現(三):通過主配置類main方法分析SpringBoot啓動配置原理

SpringBoot原理髮現(四):瞭解SpringBoot啓動中的幾個重要回調機制
 

SpringBoot 啓動配置原理

@SpringBootApplication
public class DemoApplication {

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

啓動運行流程分析

  debug上面主配置類的run方法,它會執行到如下代碼:

 由此可見,主配置類run方法分爲兩個部分。

第一步 :new SpringApplication(primarySources),創建出SrpingApplication對象

第二步 :調用返回對象SpringApplication.run(args)

  

1. new SpringApplication(primarySources)

  跟蹤new SpringApplication(primarySources),最終會執行SpringApplication的構造方法,如下:

   根據方法形參可以看出配置類其實可以傳入多個,構造函數執行流程解釋如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    
    //檢查主要來源類是否爲空,此時就是我們傳入的主配置類:class com.example.demo.DemoApplication
    Assert.notNull(primarySources, "PrimarySources must not be null");
    
    //將主要來源類保存到primarySources中,primarySources:Set<Class<?>> primarySources
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    //判斷當前應用的類型,返回WebApplicationType.SERVLET,代表是一個web應用
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    /**
     * 查找類路徑下META-INF/spring.factories 文件中定義的ApplicationContextInitializer的值列表並保存在initializers屬性中
     *   
     * 其實和 Springboot原理探究(一)
     *        2.2.2 @Import(AutoConfigurationImportSelector.class) 同理
     *  只要是getSpringFactoriesInstances()就是去查找類路徑下META-INF/spring.factories文件指定的值列表
     */
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    //同上面一樣,查找類路徑下META-INF/spring.factories 文件中定義的ApplicationListener的值列表並保存在listeners屬性中
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    //查找配置類中有main方法的主配置類
    this.mainApplicationClass = deduceMainApplicationClass();
}

 META-INF/spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

至此new SpringApplication(primarySources)執行完畢,並創建出SpringApplication對象 ,可以看出此步驟主要是將  ApplicationContextInitializer  和  ApplicationListener 保存起來

 

 2. springApplication.run(args)

  調用的run方法解釋如下:

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {

    //StopWatch開始監聽,設置IOC容器屬性null
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
    //AWT應用,忽略
    configureHeadlessProperty();
    
    //獲取類路徑下META-INF/spring.factories 文件中SpringApplicationRunListener定義的值,參見下面 2.1
    SpringApplicationRunListeners listeners = getRunListeners(args);
    
    //回調所有SpringApplicationRunListener的starting(),可以查看SpringApplicationRunListener監聽器中定義的幾個回調方法 ,參見下面 2.2
    listeners.starting();
    
    try {
        //將args封裝成ApplicationArguments對象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        //準備環境,並回調SpringApplicationRunListener的environmentPrepared方法,參見下面 2.3
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        
        //打印Spring Banner圖
        Banner printedBanner = printBanner(environment);
        
        //通過BeanUtils反射創建IOC容器,此處會根據this.webApplicationType判斷是否爲web環境,而this.webApplicationType已經在第一大步驟中得到
        context = createApplicationContext();
        
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
                
        /**  準備上下文:將environment保存在IOC容器中,
      *    並 回調所有的ApplicationContextInitializer的initialize方法, ApplicationContextInitializer則是在第一大步驟中已經獲取且保存了
* 回調所有SpringApplicationRunListenercontextPrepared方法, * 回調所有SpringApplicationRunListenercontextLoaded方法
      * 參見下面 2.4
      */ prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器:初始化IOC,初始化所有的組件,如果是web應用此處還會創建嵌入式的tomcat refreshContext(context); //2.2.9.RELEASE版本的spingboot這就是一個空方法,之前的版本中會調用callRunners方法 afterRefresh(context, applicationArguments); //StopWatch停止監聽 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //調用所有SpringApplicationRunListenerstarted方法 listeners.started(context); //從IOC容器中獲取ApplicationRunner 和 CommandLineRunner, SpringBoot中比較重要的幾個回調機制
     //先回調ApplicationRunner的run在回調CommandLineRunner
callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //回調所有SpringApplicationRunListenerrunning方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //返回IOC容器 return context; }

 

2.1 SpringApplicationRunListener 監聽器

  SpringBoot中比較重要的回調機制之一

  可以看出又是getSpringFactoriesInstances方法,只要是getSpringFactoriesInstances()就是去查找類路徑下META-INF/spring.factories文件指定的值列表,而這裏就是去獲取SpringApplicationRunListener的值列表

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

 

2.2 SpringApplicationRunListener監聽器

  回調SpringApplicationRunListener starting方法

void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }

 

2.3 prepareEnvironment(listeners, applicationArguments) 環境準備

  回調SpringApplicationRunListener 的 environmentPrepared方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
    // 創建環境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    
    //回調SpringApplicationRunListener的environmentPrepared方法,環境準備完成
    listeners.environmentPrepared(environment);
    
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

 2.4 準備上下文

  回調之前已經保存的所有ApplicationContextInitializerinitialize方法
  回調所有SpringApplicationRunListenercontextPrepared方法
  回調所有SpringApplicationRunListenercontextLoaded方法

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    
    //回調所有的ApplicationContextInitializer的initialize方法,而ApplicationContextInitializer就是之前創建SpringApplication時保存的
    applyInitializers(context);
    
    //回調所有SpringApplicationRunListener的contextPrepared方法
    listeners.contextPrepared(context);
    
    //日誌記錄
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    
    //回調所有SpringApplicationRunListener的contextLoaded方法
    listeners.contextLoaded(context);
}

 至此springApplication.run(args)執行完畢,可以看出第二大步驟就是環境準備,初始化IOC容器,掃描Bean創建組件,並調用幾個重要的回調機制。

 

SpringBoot中四個重要的回調機制

  1. ApplicationContextInitializer

    在第一大步驟中掃描META-INF/spring.factories 文件得到,在第二大步驟中回調其initialize方法

  2. SpringApplicationRunListener

    在第二大步驟中掃描META-INF/spring.factories 文件得到,並依次回調

        a. starting()
        b. environmentPrepared()
        c. contextPrepared()
        d. contextLoaded()
        e. started()
        f. running()

  3. ApplicationRunner

    在第二大步驟中,通過IOC容器獲取,且回調run方法

  4. CommandLineRunner

    在第二大步驟中,通過IOC容器獲取,且回調run方法

 

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