1. 程式人生 > >SpringBoot 原始碼解析 (二)----- Spring Boot精髓:啟動流程原始碼分析

SpringBoot 原始碼解析 (二)----- Spring Boot精髓:啟動流程原始碼分析

本文從原始碼的角度來看看Spring Boot的啟動過程到底是怎麼樣的,為何以往紛繁複雜的配置到如今可以這麼簡便。

入口類

@SpringBootApplication
public class HelloWorldMainApplication {

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

@SpringBootApplication我們上一篇文章中大概的講過了,有興趣的可以看看我第一篇關於SpringBoot的文章,本篇文章主要關注SpringApplication.run(HelloWorldMainApplication.class, args);,我們跟進去看看

// 呼叫靜態類,引數對應的就是HelloWorldMainApplication.class以及main方法中的args
public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) {
    return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return (new SpringApplication(sources)).run(args);
}

它實際上會構造一個SpringApplication的例項,並把我們的啟動類HelloWorldMainApplication.class作為引數傳進去,然後執行它的run方法

SpringApplication構造器

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //把HelloWorldMainApplication.class設定為屬性儲存起來
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //設定應用型別是Standard還是Web
    this.webApplicationType = deduceWebApplicationType();
    //設定初始化器(Initializer),最後會呼叫這些初始化器
    setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
    //設定監聽器(Listener)
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

先將HelloWorldMainApplication.class儲存在this.primarySources屬性中

設定應用型別

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

// 相關常量
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.reactive.DispatcherHandler";
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.servlet.DispatcherServlet";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
        "org.springframework.web.context.ConfigurableWebApplicationContext" };

這裡主要是通過類載入器判斷REACTIVE相關的Class是否存在,如果不存在,則web環境即為SERVLET型別。這裡設定好web環境型別,在後面會根據型別初始化對應環境。大家還記得我們第一篇文章中引入的依賴嗎?

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-web 的pom又會引入Tomcat和spring-webmvc,如下

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.5.RELEASE</version>
  <scope>compile</scope>
</dependency>

 我們來看看spring-webmvc這個jar包

很明顯spring-webmvc中存在DispatcherServlet這個類,也就是我們以前SpringMvc的核心Servlet,通過類載入能載入DispatcherServlet這個類,那麼我們的應用型別自然就是WebApplicationType.SERVLET

public enum WebApplicationType {
    NONE,
    SERVLET,
    REACTIVE;

    private WebApplicationType() {
    }
}

設定初始化器(Initializer)

//設定初始化器(Initializer),最後會呼叫這些初始化器
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));

我們先來看看getSpringFactoriesInstances( ApplicationContextInitializer.class)

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

// 這裡的入參type就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 使用Set儲存names來避免重複元素
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根據names來進行例項化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    // 對例項進行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

這裡面首先會根據入參type讀取所有的names(是一個String集合),然後根據這個集合來完成對應的例項化操作:

// 入參就是ApplicationContextInitializer.class
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
  String factoryClassName = factoryClass.getName();

  try {
      //從類路徑的META-INF/spring.factories中載入所有預設的自動配置類
      Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
      ArrayList result = new ArrayList();

      while(urls.hasMoreElements()) {
          URL url = (URL)urls.nextElement();
          Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
          //獲取ApplicationContextInitializer.class的所有值
          String factoryClassNames = properties.getProperty(factoryClassName);
          result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
      }

      return result;
  } catch (IOException var8) {
      throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
  }
}

這個方法會嘗試從類路徑的META-INF/spring.factories處讀取相應配置檔案,然後進行遍歷,讀取配置檔案中Key為:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure這個包為例,它的META-INF/spring.factories部分定義如下所示:

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

這兩個類名會被讀取出來,然後放入到Set<String>集合中,準備開始下面的例項化操作:

// parameterTypes: 上一步得到的names集合
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
        Set<String> names) {
    List<T> instances = new ArrayList<T>(names.size());
    for (String name : names) {
        try {
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            //確認被載入類是ApplicationContextInitializer的子類
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            //反射例項化物件
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            //加入List集合中
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException(
                    "Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

確認被載入的類確實是org.springframework.context.ApplicationContextInitializer的子類,然後就是得到構造器進行初始化,最後放入到例項列表中。

因此,所謂的初始化器就是org.springframework.context.ApplicationContextInitializer的實現類,這個介面是這樣定義的:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

    void initialize(C applicationContext);

}

在Spring上下文被重新整理之前進行初始化的操作。典型地比如在Web應用中,註冊Property Sources或者是啟用Profiles。Property Sources比較好理解,就是配置檔案。Profiles是Spring為了在不同環境下(如DEV,TEST,PRODUCTION等),載入不同的配置項而抽象出來的一個實體。

設定監聽器(Listener)

下面開始設定監聽器:

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

我們還是跟進程式碼看看getSpringFactoriesInstances

// 這裡的入參type是:org.springframework.context.ApplicationListener.class
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<String>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

可以發現,這個載入相應的類名,然後完成例項化的過程和上面在設定初始化器時如出一轍,同樣,還是以spring-boot-autoconfigure這個包中的spring.factories為例,看看相應的Key-Value:

org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

這10個監聽器會貫穿springBoot整個生命週期。至此,對於SpringApplication例項的初始化過程就結束了。

SpringApplication.run方法

完成了SpringApplication例項化,下面開始呼叫run方法:

public ConfigurableApplicationContext run(String... args) {
    // 計時工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    configureHeadlessProperty();

    // 第一步:獲取並啟動監聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 第二步:根據SpringApplicationRunListeners以及引數來準備環境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
        configureIgnoreBeanInfo(environment);

        // 準備Banner列印器 - 就是啟動Spring Boot的時候列印在console上的ASCII藝術字體
        Banner printedBanner = printBanner(environment);

        // 第三步:建立Spring容器
        context = createApplicationContext();

        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);

        // 第四步:Spring容器前置處理
        prepareContext(context, environment, listeners, applicationArguments,printedBanner);

        // 第五步:重新整理容器
        refreshContext(context);
     // 第六步:Spring容器後置處理 afterRefresh(context, applicationArguments);     // 第七步:發出結束執行的事件 listeners.started(context); // 第八步:執行Runners this.callRunners(context, applicationArguments); stopWatch.stop(); // 返回容器 return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } }
  • 第一步:獲取並啟動監聽器
  • 第二步:根據SpringApplicationRunListeners以及引數來準備環境
  • 第三步:建立Spring容器
  • 第四步:Spring容器前置處理
  • 第五步:重新整理容器
  • 第六步:Spring容器後置處理
  • 第七步:發出結束執行的事件
  • 第八步:執行Runners

 下面具體分析。

第一步:獲取並啟動監聽器

獲取監聽器

跟進getRunListeners方法:

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中讀取Key為org.springframework.boot.SpringApplicationRunListener的Values:

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

getSpringFactoriesInstances中反射獲取例項時會觸發EventPublishingRunListener的建構函式,我們來看看EventPublishingRunListener的建構函式:

 1 public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
 2     private final SpringApplication application;
 3     private final String[] args;
 4     //廣播器
 5     private final SimpleApplicationEventMulticaster initialMulticaster;
 6 
 7     public EventPublishingRunListener(SpringApplication application, String[] args) {
 8         this.application = application;
 9         this.args = args;
10         this.initialMulticaster = new SimpleApplicationEventMulticaster();
11         Iterator var3 = application.getListeners().iterator();
12 
13         while(var3.hasNext()) {
14             ApplicationListener<?> listener = (ApplicationListener)var3.next();
15             //將上面設定到SpringApplication的十一個監聽器全部新增到SimpleApplicationEventMulticaster這個廣播器中
16             this.initialMulticaster.addApplicationListener(listener);
17         }
18 
19     }
20     //略...
21 }

我們看到EventPublishingRunListener裡面有一個廣播器,EventPublishingRunListener 的構造方法將SpringApplication的十一個監聽器全部新增到SimpleApplicationEventMulticaster這個廣播器中,我們來看看是如何新增到廣播器:

 1 public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
 2     //廣播器的父類中存放儲存監聽器的內部內
 3     private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);
 4 
 5     @Override
 6     public void addApplicationListener(ApplicationListener<?> listener) {
 7         synchronized (this.retrievalMutex) {
 8             Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
 9             if (singletonTarget instanceof ApplicationListener) {
10                 this.defaultRetriever.applicationListeners.remove(singletonTarget);
11             }
12             //內部類物件
13             this.defaultRetriever.applicationListeners.add(listener);
14             this.retrieverCache.clear();
15         }
16     }
17 
18     private class ListenerRetriever {
19         //儲存所有的監聽器
20         public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
21         public final Set<String> applicationListenerBeans = new LinkedHashSet();
22         private final boolean preFiltered;
23 
24         public ListenerRetriever(boolean preFiltered) {
25             this.preFiltered = preFiltered;
26         }
27 
28         public Collection<ApplicationListener<?>> getApplicationListeners() {
29             LinkedList<ApplicationListener<?>> allListeners = new LinkedList();
30             Iterator var2 = this.applicationListeners.iterator();
31 
32             while(var2.hasNext()) {
33                 ApplicationListener<?> listener = (ApplicationListener)var2.next();
34                 allListeners.add(listener);
35             }
36 
37             if (!this.applicationListenerBeans.isEmpty()) {
38                 BeanFactory beanFactory = AbstractApplicationEventMulticaster.this.getBeanFactory();
39                 Iterator var8 = this.applicationListenerBeans.iterator();
40 
41                 while(var8.hasNext()) {
42                     String listenerBeanName = (String)var8.next();
43 
44                     try {
45                         ApplicationListener<?> listenerx = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
46                         if (this.preFiltered || !allListeners.contains(listenerx)) {
47                             allListeners.add(listenerx);
48                         }
49                     } catch (NoSuchBeanDefinitionException var6) {
50                         ;
51                     }
52                 }
53             }
54 
55             AnnotationAwareOrderComparator.sort(allListeners);
56             return allListeners;
57         }
58     }
59     //略...
60 }

上述方法定義在SimpleApplicationEventMulticaster父類AbstractApplicationEventMulticaster中。關鍵程式碼為this.defaultRetriever.applicationListeners.add(listener);,這是一個內部類,用來儲存所有的監聽器。也就是在這一步,將spring.factories中的監聽器傳遞到SimpleApplicationEventMulticaster中。我們現在知道EventPublishingRunListener中有一個廣播器SimpleApplicationEventMulticaster,SimpleApplicationEventMulticaster廣播器中又存放所有的監聽器。

啟動監聽器

我們上面一步通過getRunListeners方法獲取的監聽器為EventPublishingRunListener,從名字可以看出是啟動事件釋出監聽器,主要用來發布啟動事件。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    private final SpringApplication application;
    private final String[] args;
    private final SimpleApplicationEventMulticaster initialMulticaster;

我們先來看看SpringApplicationRunListener這個介面

package org.springframework.boot;
public interface SpringApplicationRunListener {

    // 在run()方法開始執行時,該方法就立即被呼叫,可用於在初始化最早期時做一些工作
    void starting();
    // 當environment構建完成,ApplicationContext建立之前,該方法被呼叫
    void environmentPrepared(ConfigurableEnvironment environment);
    // 當ApplicationContext構建完成時,該方法被呼叫
    void contextPrepared(ConfigurableApplicationContext context);
    // 在ApplicationContext完成載入,但沒有被重新整理前,該方法被呼叫
    void contextLoaded(ConfigurableApplicationContext context);
    // 在ApplicationContext重新整理並啟動後,CommandLineRunners和ApplicationRunner未被呼叫前,該方法被呼叫
    void started(ConfigurableApplicationContext context);
    // 在run()方法執行完成前該方法被呼叫
    void running(ConfigurableApplicationContext context);
    // 當應用執行出錯時該方法被呼叫
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

SpringApplicationRunListener介面在Spring Boot 啟動初始化的過程中各種狀態時執行,我們也可以新增自己的監聽器,在SpringBoot初始化時監聽事件執行自定義邏輯,我們先來看看SpringBoot啟動時第一個啟動事件listeners.starting():

@Override
public void starting() {
    //關鍵程式碼,先建立application啟動事件`ApplicationStartingEvent`
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

這裡先建立了一個啟動事件ApplicationStartingEvent,我們繼續跟進SimpleApplicationEventMulticaster,有個核心方法:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    //通過事件型別ApplicationStartingEvent獲取對應的監聽器
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        //獲取執行緒池,如果為空則同步處理。這裡執行緒池為空,還未沒初始化。
        Executor executor = getTaskExecutor();
        if (executor != null) {
            //非同步傳送事件
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            //同步傳送事件
            invokeListener(listener, event);
        }
    }
}

這裡會根據事件型別ApplicationStartingEvent獲取對應的監聽器,在容器啟動之後執行響應的動作,有如下4種監聽器:

我們選擇springBoot 的日誌監聽器來進行講解,核心程式碼如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    //在springboot啟動的時候
    if (event instanceof ApplicationStartedEvent) {
        onApplicationStartedEvent((ApplicationStartedEvent) event);
    }
    //springboot的Environment環境準備完成的時候
    else if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent(
                (ApplicationEnvironmentPreparedEvent) event);
    }
    //在springboot容器的環境設定完成以後
    else if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent((ApplicationPreparedEvent) event);
    }
    //容器關閉的時候
    else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
            .getApplicationContext().getParent() == null) {
        onContextClosedEvent();
    }
    //容器啟動失敗的時候
    else if (event instanceof ApplicationFailedEvent) {
        onApplicationFailedEvent();
    }
}

因為我們的事件型別為ApplicationEvent,所以會執行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot會在執行過程中的不同階段,傳送各種事件,來執行對應監聽器的對應方法。

第二步:環境構建

ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

跟進去該方法:

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    //獲取對應的ConfigurableEnvironment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //釋出環境已準備事件,這是第二次釋出事件
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    ConfigurationPropertySources.attach(environment);
    return environment;
}

來看一下getOrCreateEnvironment()方法,前面已經提到,environment已經被設定了servlet型別,所以這裡建立的是環境物件是StandardServletEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    if (this.webApplicationType == WebApplicationType.SERVLET) {
        return new StandardServletEnvironment();
    }
    return new StandardEnvironment();
}

接下來看一下listeners.environmentPrepared(environment);,上面已經提到了,這裡是第二次釋出事件。什麼事件呢?來看一下根據事件型別獲取到的監聽器:

主要來看一下ConfigFileApplicationListener,該監聽器非常核心,主要用來處理專案配置。專案中的 properties 和yml檔案都是其內部類所載入。具體來看一下:

首先還是會去讀spring.factories 檔案,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();獲取的處理類有以下四種:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

在執行完上述三個監聽器流程後,ConfigFileApplicationListener會執行該類本身的邏輯。由其內部類Loader載入專案制定路徑下的配置檔案:

private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";

至此,專案的變數配置已全部載入完畢,來一起看一下:

這裡一共6個配置檔案,取值順序由上到下。也就是說前面的配置變數會覆蓋後面同名的配置變數。專案配置變數的時候需要注意這點。

第三步:建立容器

context = createApplicationContext();

繼續跟進該方法:

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

這裡建立容器的型別 還是根據webApplicationType進行判斷的,該型別為SERVLET型別,所以會通過反射裝載對應的位元組碼,也就是AnnotationConfigServletWebServerApplicationContext

第四步:Spring容器前置處理

這一步主要是在容器重新整理之前的準備動作。包含一個非常關鍵的操作:將啟動類注入容器,為後續開啟自動化配置奠定基礎。

prepareContext(context, environment, listeners, applicationArguments,printedBanner);

繼續跟進該方法:

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    //設定容器環境,包括各種變數
    context.setEnvironment(environment);
    //執行容器後置處理
    postProcessApplicationContext(context);
    //執行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的例項)
    applyInitializers(context);
  //傳送容器已經準備好的事件,通知各監聽器
    listeners.contextPrepared(context);

    //註冊啟動引數bean,這裡將容器指定的引數封裝成bean,注入容器
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
    //設定banner
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    //獲取我們的啟動類指定的引數,可以是多個
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //載入我們的啟動類,將啟動類注入容器
    load(context, sources.toArray(new Object[0]));
    //釋出容器已載入事件。
    listeners.contextLoaded(context);
}

呼叫初始化器

protected void applyInitializers(ConfigurableApplicationContext context) {
    // 1. 從SpringApplication類中的initializers集合獲取所有的ApplicationContextInitializer
    for (ApplicationContextInitializer initializer : getInitializers()) {
        // 2. 迴圈呼叫ApplicationContextInitializer中的initialize方法
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                initializer.getClass(), ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        initializer.initialize(context);
    }
}

這裡終於用到了在建立SpringApplication例項時設定的初始化器了,依次對它們進行遍歷,並呼叫initialize方法。我們也可以自定義初始化器,並實現initialize方法,然後放入META-INF/spring.factories配置檔案中Key為:org.springframework.context.ApplicationContextInitializer的value中,這裡我們自定義的初始化器就會被呼叫,是我們專案初始化的一種方式

載入啟動指定類(重點)

大家先回到文章最開始看看,在建立SpringApplication例項時,先將HelloWorldMainApplication.class儲存在this.primarySources屬性中,現在就是用到這個屬性的時候了,我們來看看getAllSources()

public Set<Object> getAllSources() {
    Set<Object> allSources = new LinkedHashSet();
    if (!CollectionUtils.isEmpty(this.primarySources)) {
        //獲取primarySources屬性,也就是之前儲存的HelloWorldMainApplication.class
        allSources.addAll(this.primarySources);
    }

    if (!CollectionUtils.isEmpty(this.sources)) {
        allSources.addAll(this.sources);
    }

    return Collections.unmodifiableSet(allSources);
}

很明顯,獲取了this.primarySources屬性,也就是我們的啟動類HelloWorldMainApplication.class,我們接著看load(context, sources.toArray(new Object[0]));

protected void load(ApplicationContext context, Object[] sources) {
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}

private int load(Class<?> source) {
    if (isGroovyPresent()
            && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                GroovyBeanDefinitionSource.class);
        load(loader);
    }
    if (isComponent(source)) {
        //以註解的方式,將啟動類bean資訊存入beanDefinitionMap,也就是將HelloWorldMainApplication.class存入了beanDefinitionMap
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

啟動類HelloWorldMainApplication.class被載入到 beanDefinitionMap中,後續該啟動類將作為開啟自動化配置的入口,後面一篇文章我會詳細的分析,啟動類是如何載入,以及自動化配置開啟的詳細流程。

通知監聽器,容器已準備就緒

listeners.contextLoaded(context);

主還是針對一些日誌等監聽器的響應處理。

第五步:重新整理容器

執行到這裡,springBoot相關的處理工作已經結束,接下的工作就交給了spring。我們來看看refreshContext(context);

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    //呼叫建立的容器applicationContext中的refresh()方法
    ((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        /**
         * 重新整理上下文環境
         */
        prepareRefresh();

        /**
         * 初始化BeanFactory,解析XML,相當於之前的XmlBeanFactory的操作,
         */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        /**
         * 為上下文準備BeanFactory,即對BeanFactory的各種功能進行填充,如常用的註解@Autowired @Qualifier等
         * 新增ApplicationContextAwareProcessor處理器
         * 在依賴注入忽略實現*Aware的介面,如EnvironmentAware、ApplicationEventPublisherAware等
         * 註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的例項注入進去
         */
        prepareBeanFactory(beanFactory);

        try {
            /**
             * 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
             */
            postProcessBeanFactory(beanFactory);

            /**
             * 啟用各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
             * 執行對應的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
             */
            invokeBeanFactoryPostProcessors(beanFactory);

            /**
             * 註冊攔截Bean建立的Bean處理器,即註冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意兩者的區別
             * 注意,這裡僅僅是註冊,並不會執行對應的方法,將在bean的例項化時執行對應的方法
             */
            registerBeanPostProcessors(beanFactory);

            /**
             * 初始化上下文中的資原始檔,如國際化檔案的處理等
             */
            initMessageSource();

            /**
             * 初始化上下文事件廣播器,並放入applicatioEventMulticaster,如ApplicationEventPublisher
             */
            initApplicationEventMulticaster();

            /**
             * 給子類擴充套件初始化其他Bean
             */
            onRefresh();

            /**
             * 在所有bean中查詢listener bean,然後註冊到廣播器中
             */
            registerListeners();

            /**
             * 設定轉換器
             * 註冊一個預設的屬性值解析器
             * 凍結所有的bean定義,說明註冊的bean定義將不能被修改或進一步的處理
             * 初始化剩餘的非惰性的bean,即初始化非延遲載入的bean
             */
            finishBeanFactoryInitialization(beanFactory);

            /**
             * 通過spring的事件釋出機制釋出ContextRefreshedEvent事件,以保證對應的監聽器做進一步的處理
             * 即對那種在spring啟動後需要處理的一些類,這些類實現了ApplicationListener<ContextRefreshedEvent>,
             * 這裡就是要觸發這些類的執行(執行onApplicationEvent方法)
             * 另外,spring的內建Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
             * 完成初始化,通知生命週期處理器lifeCycleProcessor重新整理過程,同時發出ContextRefreshEvent通知其他人
             */
            finishRefresh();
        }

        finally {
    
            resetCommonCaches();
        }
    }
}

refresh方法在spring整個原始碼體系中舉足輕重,是實現 ioc 和 aop的關鍵。我之前也有文章分析過這個過程,大家可以去看看

第六步:Spring容器後置處理

protected void afterRefresh(ConfigurableApplicationContext context,
        ApplicationArguments args) {
}

擴充套件介面,設計模式中的模板方法,預設為空實現。如果有自定義需求,可以重寫該方法。比如列印一些啟動結束log,或者一些其它後置處理。

第七步:發出結束執行的事件

public void started(ConfigurableApplicationContext context) {
    //這裡就是獲取的EventPublishingRunListener
    Iterator var2 = this.listeners.iterator();

    while(var2.hasNext()) {
        SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
        //執行EventPublishingRunListener的started方法
        listener.started(context);
    }
}

public void started(ConfigurableApplicationContext context) {
    //建立ApplicationStartedEvent事件,並且釋出事件
    //我們看到是執行的ConfigurableApplicationContext這個容器的publishEvent方法,和前面的starting是不同的
    context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}

獲取EventPublishingRunListener監聽器,並執行其started方法,並且將建立的Spring容器傳進去了,建立一個ApplicationStartedEvent事件,並執行ConfigurableApplicationContext 的publishEvent方法,也就是說這裡是在Spring容器中釋出事件,並不是在SpringApplication中釋出事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的11個監聽器釋出啟動事件。

第八步:執行Runners

我們再來看看最後一步callRunners(context, applicationArguments);

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<Object>();
    //獲取容器中所有的ApplicationRunner的Bean例項
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    //獲取容器中所有的CommandLineRunner的Bean例項
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<Object>(runners)) {
        if (runner instanceof ApplicationRunner) {
            //執行ApplicationRunner的run方法
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            //執行CommandLineRunner的run方法
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

如果是ApplicationRunner的話,則執行如下程式碼:

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        runner.run(args);
    } catch (Exception var4) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", var4);
    }
}

如果是CommandLineRunner的話,則執行如下程式碼:

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        runner.run(args.getSourceArgs());
    } catch (Exception var4) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", var4);
    }
}

我們也可以自定義一些ApplicationRunner或者CommandLineRunner,實現其run方法,並注入到Spring容器中,在SpringBoot啟動完成後,會執行所有的runner的run方法

至此,SpringApplication大概分析了一遍,還有很多細節和核心留在下面文章中講。