天天看點

Spring boot 底層核心源碼剖析之啟動過程分析(1)

作者:嗯哼QQ

Spring Boot 啟動過程可以分為三個主要步驟:準備階段、應用上下文建立階段和重新整理階段。Spring Boot 啟動過程的源碼主要位于 spring-boot-autoconfigure、spring-boot-starter、spring-boot 等子產品中。下面是一個簡要的啟動過程源碼分析:

本次Spring Boot是基于2.1.0 分析。Spring boot 每個版本有些變化,讀者盡量和我保持一緻,以防源碼有些出入。

一.啟動過程

  1. 準備階段

在準備階段,Spring Boot 會讀取應用程式的配置資訊并加載到 Spring 環境中。這部分的源碼主要位于 spring-boot-autoconfigure 子產品中的 AutoConfigurationImportSelector 類中。在這個類中,首先會加載預設的配置檔案,如 application.properties,然後會讀取使用者自定義的配置檔案,如 application.yml。這些配置資訊會被封裝成一個 Environment 對象,并且會注冊到 Spring 環境中。同時,這個類還會掃描所有的 Spring Boot 自動配置類,将這些自動配置類注冊到 Spring 環境中。

  1. 應用上下文建立階段

在應用上下文建立階段,Spring Boot 會根據讀取到的配置資訊建立應用程式上下文。這部分的源碼主要位于 spring-boot 子產品中的 SpringApplication 類中。在這個類中,首先會建立一個 ApplicationContextInitializer 對象,并将其注冊到應用程式上下文中。然後,這個類會根據讀取到的配置資訊建立一個 ApplicationContext 對象,并将其設定為主應用程式上下文。接着,這個類會檢測是否有 Web 應用程式上下文,并根據需要建立出相應的 Web 應用程式上下文。這個過程中,Spring Boot 會掃描所有的 Bean 定義,并根據這些 Bean 定義建立出相應的 Bean 執行個體。

  1. 重新整理階段

在重新整理階段,Spring Boot 會将應用程式上下文中的所有 Bean 進行初始化和依賴注入。同時,Spring Boot 還會執行各種初始化操作,如加載資料庫驅動、建立資料庫連接配接池等。這部分的源碼主要位于 spring-context、spring-core 等子產品中。在初始化完成後,Spring Boot 會啟動内嵌的 Tomcat 伺服器或其他 Web 伺服器,将 Web 應用程式釋出到伺服器上。當應用程式啟動完成後,Spring Boot 會将控制權交給應用程式,等待處理請求。

二.源碼分析

建立SpringApplication

啟動類入口SpringApplication

Spring boot 底層核心源碼剖析之啟動過程分析(1)
Spring boot 底層核心源碼剖析之啟動過程分析(1)
Spring boot 底層核心源碼剖析之啟動過程分析(1)

1.設定應用類型

這個過程非常重要,直接決定了項目的類型,應用類型分為三種,都在WebApplicationType這個枚舉類中,如下:

  1. NONE:顧名思義,什麼都沒有,正常流程走,不額外的啟動web容器,比如Tomcat。
  2. SERVLET:基于servlet的web程式,需要啟動内嵌的servletweb容器,比如Tomcat。
  3. REACTIVE:基于reactive的web程式,需要啟動内嵌reactiveweb容器

2.設定初始化器(Initializer)

Spring boot 底層核心源碼剖析之啟動過程分析(1)

初始化器ApplicationContextInitializer 第一步擷取初始化器的名稱,詳細源碼在loadFactoryNames()方法中了,跟着源碼進入,最終調用的是#SpringFactoriesLoader.loadSpringFactories()方法。loadSpringFactories()方法就不再詳細解釋了,其實就是從類路徑META-INF/spring.factories中加載ApplicationContextInitializer的值。在spring-boot-autoconfigure的spring.factories檔案中的值如下圖:

Spring boot 底層核心源碼剖析之啟動過程分析(1)
Spring boot 底層核心源碼剖析之啟動過程分析(1)

3.設定監聽器(Listener)

監聽器(ApplicationListener)主要用于監聽特定的事件(ApplicationEvent),比如IOC容器重新整理、容器關閉等。Spring Boot擴充了ApplicationEvent建構了SpringApplicationEvent這個抽象類,主要用于Spring Boot啟動過程中觸發的事件,比如程式啟動中、程式啟動完成等。

Spring boot 底層核心源碼剖析之啟動過程分析(1)

從源碼中知道其實和初始化器(ApplicationContextInitializer)執行的是同一個方法,同樣是從META-INF/spring.factories檔案中擷取。在spring-boot-autoconfigure的spring.factories

Spring boot 底層核心源碼剖析之啟動過程分析(1)

總結:

SpringApplication的建構都是為了run()方法啟動做鋪墊,最重要的部分就是設定應用類型、設定初始化器、設定監聽器。(「注意」:初始化器和這裡的監聽器都要放置在spring.factories檔案中才能在這一步驟加載,否則不會生效,是以此時IOC容器還未建立,即使将其注入到IOC容器中也是不會生效的)。

Spring boot 底層核心源碼剖析之啟動過程分析(1)

執行run()方法

Spring boot 底層核心源碼剖析之啟動過程分析(1)

擷取、啟動運作過程監聽器

//從spring.factories中擷取監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);           

getRunListeners()方法,其實還是調用了loadFactoryNames()方法從spring.factories檔案中擷取值,如下:

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

最終注入的是EventPublishingRunListener這個實作類,建立執行個體過程肯定是通過反射了,是以我們看看它的構造方法

Spring boot 底層核心源碼剖析之啟動過程分析(1)
Spring boot 底層核心源碼剖析之啟動過程分析(1)
//執行starting()方法
listeners.starting(bootstrapContext, this.mainApplicationClass);           

執行SpringApplicationRunListeners的starting()方法,執行了multicastEvent()方法,廣播了ApplicationStartingEvent事件。周遊ApplicationListener的實作類,找到監聽ApplicationStartingEvent這個事件的監聽器,執行onApplicationEvent()方法。

2.環境建構

加載了系統環境配置、使用者自定義配置; 廣播ApplicationEnvironmentPreparedEvent事件,觸發監聽器。

//要用于加載系統配置以及使用者的自定義配置(application.properties)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);           
Spring boot 底層核心源碼剖析之啟動過程分析(1)

3.建立IOC容器

context = createApplicationContext();
=================================
  
	/**
	 * Strategy method used to create the {@link ApplicationContext}. By default this
	 * method will respect any explicitly set application context or application context
	 * class before falling back to a suitable default.
	 * @return the application context (not yet refreshed)
	 * @see #setApplicationContextClass(Class)
	 */
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
          //webApplicationType決定建立的類型
          //這裡的是servlet,是以建立的是AnnotationConfigServletWebServerApplicationContext  
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_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);
	}           

4.IOC容器的前置處理

prepareContext(context, environment, listeners, applicationArguments,printedBanner);           
Spring boot 底層核心源碼剖析之啟動過程分析(1)

調用初始化器

/**
 * Apply any {@link ApplicationContextInitializer}s to the context before it is
 * refreshed.
 * @param context the configured ApplicationContext (not refreshed yet)
 * @see ConfigurableApplicationContext#refresh()
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
  //在SpringApplication建構過程中設定的初始化器,從spring.factories取值的周遊執行
  //将自定義的ApplicationContextInitializer放在META-INF/spring.factories中,在此時也是會被調用
   for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
            initializer.getClass(), ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
   }
}           

加載啟動類,注入容器

// Load the sources
//在SpringApplication建構過程中将主啟動類放置在primarySources這個集合中,
// 此時的getAllSources()即是從其中取值
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//取出主啟動類,當然你的項目中可能不止一個,将其加載到IOC容器中
//将主啟動類加載到beanDefinitionMap,後續該啟動類将作為開啟自動配置化的入口
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);           

兩次廣播事件

兩次事件廣播,分别是ApplicationContextInitializedEvent和ApplicationPreparedEvent

listeners.contextPrepared(context);           

5.重新整理容器

重新整理容器,比如初始化資源,初始化上下文廣播器等。

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();
        }
    }
}           

6.IOC容器的後置處理

afterRefresh(context, applicationArguments);           

7.發出結束執行的事件

listeners.started(context);           

同樣是EventPublishingRunListener這個監聽器,廣播ApplicationStartedEvent事件。但是這裡廣播事件和前幾次不同,并不是廣播給SpringApplication中的監聽器(在建構過程中從spring.factories檔案擷取的監聽器)。是以在IOC容器中注入的監聽器(使用@Component等方式注入的)也能夠生效。前面幾個事件隻有在spring.factories檔案中設定的監聽器才會生效。這裡并沒有用事件廣播器SimpleApplicationEventMulticaster廣播事件,而是使用ConfigurableApplicationContext直接在IOC容器中釋出事件。

8.執行Runners

callRunners(context, applicationArguments);           

callRunners(context, applicationArguments),它用于調用 Runner。Runner 是 Spring Boot 中的一個重要概念,它是一個用于執行特定任務的元件。在應用程式啟動時,Spring Boot 會自動掃描并調用所有實作了 CommandLineRunner 和 ApplicationRunner 接口的 Bean,執行它們的 run 方法。這些 Bean 可以用來初始化資料、建立測試資料等操作。