SpringBoot原始碼學習3——SpringBoot啟動流程

Cuzzz發表於2023-01-18

系列文章目錄和關於我

一丶前言

在 《SpringBoot原始碼學習1——SpringBoot自動裝配原始碼解析+Spring如何處理配置類的》中我們學習了SpringBoot自動裝配如何實現的,在 《Spring原始碼學習筆記12——總結篇IOC,Bean的生命週期,三大擴充套件點》我們總結了Spring IOC的底層原理。

但是我們還是不知道SpringApplication.run(主類.class, args)到底做了哪些事情。本文將和大家一起看看SpringBoot啟動的大致流程,探討SpringBoot留給我們的擴充套件介面

二丶SpringBoot啟動流程分析

image-20230118213312159

上面是SpringBoot呼叫SpringApplication.run(主類.class, args)啟動的原始碼,原始碼並不複雜,整體流程大概如下

image-20230118213655977

下面我們依據此圖,看看這些步驟SpringBoot底層原始碼

1.獲取SpringApplicationRunListener實現類,包裝成SpringApplicationRunListeners

image-20230118213933916

  • SpringApplicationRunListener是SpringBoot框架中的監聽器,在SpringBoot啟動到達對應階段的時候,會回撥starting,started等方法。

    為什麼SpringBoot不適應Spring 裡面的ApplicationListener暱,因為ApplicationListener依賴於Spring容器,@EventListener註解需要EventListenerMethodProcessor這個BeanFactoryPostProcessor掃描,將對應的bean和方法包裝成ApplicationListener註冊到ApplicationContext中(最終註冊到ApplicationEventMulticaster事件多播器中)對於ApplicationListener型別bean則直接走註冊到ApplicationContext的流程,整個流程只有Spring 容器啟動後才能進行,如果沒有SpringApplicationRunListener則開發者無法在SpringBoot啟動對應階段進行一些擴充套件邏輯的回撥。

  • SpringApplicationRunListeners 可以看成是SpringApplicationRunListener的門面(門面設計模式)

其使用List<SpringApplicationRunListener>持有所有的SpringApplicationRunListener,然後starting等方法都是迴圈呼叫,集合中SpringApplicationRunListener對應的方法

image-20230118214801860

image-20230118214958185

  • SpringBoot如何獲取所有的SpringApplciationListener

    image-20230118215153502

    這裡將從META-INF/spring.factories獲取org.springframework.boot.SpringApplicationRunListener 定義的實現類全限定類名,然後反射呼叫構造方法(SpringApplication application, String[] args)進行例項化。隨後將根據@Order 或者 Ordered介面定義的順序進行排序,然後包裝成SpringApplicationRunListeners

    image-20230118215449974

    注意無法使用@Component註解 標註在SpringApplciationListener註解上,來實現事件的監聽,必須在META-INF/spring.factories中定義,並且必須具備構造方法(SpringApplication application, String[] args)

  • EventPublishingRunListener

    image-20230118215948246

    SpringApplication#addListeners 允許我們註冊ApplicationListener到SpringBoot中,然後EventPublishingRunListener其內部會new 一個簡單的事件多播器SimpleApplicationEventMulticaster,在對應的SpringBoot啟動階段,推送事件。下面式如何註冊ApplicationListener

    image-20230118220126682

    注意這些ApplicationListener不會被註冊到Spring上下文中,意味著不會響應Spring上下文推送的事件,除非這個ApplicationListener是一個Spring Bean 並且被Spring管理。

    下圖是EventPublishingRunListener在SpringBoot啟動的不同階段,推送事件

    image-20230118220607326

2.SpringApplicationListeners#starting

image-20230118220739167

沒啥好說的,迴圈回撥SpringApplicationRunListener#starting方法

3.prepareEnvironment 根據專案選擇Environment實現類,並例項化

在這一步,SpringBoot會根據類路徑中的類選擇一個Environment並例項化,並且根據當前啟用的配置,選擇對應的配置檔案,進行解析,並儲存到Environment中。下面是SpringBoot選擇Environment的原始碼

image-20230118221247623

那麼SpringBoot是如何判斷當前專案是什麼應用型別暱?

其實根據類路徑下是否具備指定的類,然後得到指定型別,一般我們都是servlet應用,會選擇StandardServletEnvironment

image-20230118221500605

4.SpringApplicationListeners#environmentPrepared

2.SpringApplicationListeners#starting

5.createApplicationContext

根據類路徑指定類推斷使用什麼ConfigurableApplicationContext(一般servlet應用使用AnnotationConfigServletWebServerApplicationContext)然後例項化AnnotationConfigServletWebServerApplicationContext

AnnotationConfigServletWebServerApplicationContext#onRefresh方法在Spring容器重新整理後會被呼叫,這個方法將啟動Tomcat內嵌伺服器

6.prepareContext

這個方法主要會做以下操作

  • 回撥ApplicationContextInitializer#initialize
  • 回撥所有SpringApplicationRunListener#contextPrepared
  • 將主類包裝成BeanDefinition,註冊到Spring容器上下文中
  • 回撥所有SpringApplicationRunListener#contextLoaded

利用SpringApplicationRunListeners回撥SpringApplicationRunListener,同2,不在贅述

6.1從META-INFO/spring.factories中拿所有ApplicationContextInitializer然後回撥initialize方法

image-20230118223132300

在spring上下文refresh方法呼叫前,會回撥initialize方法

image-20230118223118520

這裡呼叫前還會判斷ApplicationContextInitializer定義的泛型,保證5這一步建立的上下文,符合泛型的要求

6.2 將主類包裝成BeanDefinition,註冊到Spring容器上下文中

這一步非常重要,主類上的註解@SpringBootApplication需要ConfigurationClassPostProcessor解析,才能發揮@Import,@ComponentScan的作用,想要ConfigurationClassPostProcessor處理主類的前提是主類的BeanDefinition需要在Spring容器中。

也就是說SpringBoot的自動裝配,和掃描包路徑下的Spring 元件的前提是,主類的BeanDefinition在Spring容器中

image-20230118225311194

這裡的BeanDefinitionRegistry,其實就是來自5這一步的ApplicationContext,一般來說AnnotationConfigServletWebServerApplicationContext內部持有了一個DefaultListableBeanFactory,DefaultListableBeanFactoryBeanDefinitionRegistry的實現類,其底層使用一個ConcurrentHashMap維護,key是bean的名稱,value是對應的BeanDefinition

image-20230118230101275

當資源是一個Class的時候,會使用AnnotatedBeanDefinitionReader讀取Class物件,生成BeanDefinition

這一步還支援xml的方式

7.回撥SpringApplicationRunListener#contextLoaded

同2

8.重新整理Spring容器上下文

Spring原始碼學習筆記12——總結篇IOC,Bean的生命週期,三大擴充套件點》這篇部落格做了詳細的分析

這裡會進行自動裝配和包路徑掃描註冊BeanDefinition,然後例項化單例bean

9.回撥SpringApplicationRunListener#started

同2

10.callRunners

從spring容器中拿到ApplicationRunner,和CommandLineRunner呼叫run方法

image-20230118231725448

三丶SpringApplication,ApplicationContext,BeanFactory 三平面

我們將SpringApplication看作是SpringBoot平面,ApplicationContext看作是Spring平面,BeanFactory看作是Bean工廠平面,SpringBoot啟動到觸發spring容器重新整理,然後觸發BeanFactory例項化所有單例,非懶載入bean的流程如下

image-20230118233544314

相關文章