Spring原始碼學習筆記12——總結篇,IOC,Bean的生命週期,三大擴充套件點

2022-09-06 21:01:48

Spring原始碼學習筆記12——總結篇,IOC,Bean的生命週期,三大擴充套件點

參考了Spring 官網檔案
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
一個IOC講得很好的部落格
https://blog.csdn.net/ivan820819/article/details/79744797

之前總結的Spring文章比較水,這次好好來
文章需要有些Spring原始碼基礎(可以看我的水文)

系列文章目錄和關於我

一丶什麼是 IOC 和DI

IoC也被稱為依賴注入DI,物件在被構造方法構造,或者工廠方法創造返回,僅通過建構函式引數、工廠方法的引數,或者物件設定的屬性來定義他們的關係(即與它們一起工作的其他物件)。

  • 什麼叫依賴——物件A需要物件B一起完成工作,這種關係叫做依賴

  • IoC是如何描述依賴的

    • 構造方法的引數
    • 產生物件的工廠方法的引數
    • 物件設定的屬性

IoC容器在bean被創造後將注入這些依賴,從根本上說這個過程就叫做反轉(因此得名控制反轉)

IoC理論出現之前,我們定義的每一個物件之間的關係是這樣的,這裡面齒輪之間的咬合象徵著"依賴",ABCD四個物件都依賴與彼此,耦合度很高。

IoC理論出現後,多了一箇中介軟體——IoC容器(萬能的解耦方案,耦合度高了就加一層)

IoC容器的好處在於,ABCD四個物件並不是直接依賴的,沒有了直接的依賴關係,齒輪的傳動交給了IoC容器,IoC容器獲得了控制權,控制權從物件本身交給了IoC容器,IoC容器如同一個粘合劑。

對於傳統的方式,我們在範例化A的時候一定要在構造方法或者其他地方,讓A範例化B並且持有B的參照,這就好比物件A控制了獲取物件B的過程。但是引入了IoC之後,IoC容器負責在範例化A之後為A注入物件B(或者在構造物件A的時候,將B作為構造方法的引數,工廠方法的引數)物件A從主動獲取物件B,變成了IoC容器控制物件A獲取物件B——這就叫做控制反轉。

DI(依賴注入)是實現IoC的一種方式——由IOC容器在執行期間,動態地將某種依賴關係注入到物件之中。

二丶Spring Bean ,BeanDefinition,BeanDefinitionReader,BeanDefinitionRegistry

1.什麼是spring bean

Spring Bean是指Spring 容器管理的物件,一個Spring 容器管理多個Spring bean,對它們進行範例化,依賴注入,初始化等。這些 bean 是使用我們提供給spring容器的設定後設資料建立的(例如,以 XML <bean/>定義的形式)。

在容器中,這些 bean 定義表示為BeanDefinition 物件,其中包含以下後設資料:

  • 一個包限定的類名:通常是被定義的 bean 的全限定類名,實際實現類。
  • Bean 行為設定元素,它說明 bean 在容器中的行為方式(作用域、生命週期回撥等)。
  • 當前bean 依賴的其他bean。
  • 等等

2.BeanDefinition

BeanDefinition 描述了一個 bean 範例,它記錄了bean的屬性值、建構函式引數值以及其他更多資訊。BeanDefinition在Spring 管理bean中至關重要,指導了Spring 如何依據BeanDefinition生成Bean,bean的作用域,是否懶載入等等。

BeanDefinition中具有設定和獲取 此bean 定義的父BeanDefinition的名稱(如果有)bean 類名,Bean的作用域Bean是否懶載入當前bean依賴的bean名稱是否Autowire候選者是否是最主要的自動裝配候選者初始化方法名稱銷燬方法名稱是否單例是否原型bean屬性值

傳統的基於XML Spring 容器便是使用對於的BeanDefinitionReader解析XML將xml中的資訊包裝成BeanDefinition儲存到Spring容器中,後續由Spring容器根據BeanDefinition對Bean進行管理。

3.BeanDefinitionReader

Bean定義資訊讀取器,定義瞭如下方法

BeanDefinitionReader的典型實現莫過於XmlBeanDefinitionReader,它負責解析xml生成BeanDefinition並註冊(spring原始碼學習筆記1——解析xml生成BeanDefinition的過程解析

這個類關係圖中有兩個異類AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner,它們沒有實現BeanDefinitionReader但是存在的目的和BeanDefinitionReader是一致的——將生成BeanDefinition並註冊到容,並且根據設定生成beanDefinition的動作交給他們,並不是直接讓BeanFactory來完成,更加可延伸,更加單一職責。

4.BeanDefinitionRegistry

Bean定義註冊中心,主要是對BeanDefinition的增刪改查,自然內部會對BeanDefinition資訊進行儲存。

DefaultListableBeanFactory是一個Bean工廠,Spring中很多上下文都是通過組合它來實現對Bean的管理。其中還涉及到AliasRegistry顧名思義就是對bean的別名進行管理。

三丶BeanFactory類結構體系

Spring 容器的根介面,主要提供了getBean(根據bean名稱,型別等獲取bean的方法),以及判斷bean是否單例,是否原型等方法。Spring建議BeanFactory的實現類儘可能的支援bean的生命週期介面的回撥(比如InitializingBean#afterPropertiesSet等)其中最關鍵的實現類莫過於DefaultListableBeanFactory

1.從DefaultListableBeanFactory看BeanFactory類結構體系

  • HierarchicalBeanFactory層次結構的BeanFactory提供兩個方法getParentBeanFactory獲取當前BeanFactory的父工廠,containsLocalBean當前BeanFactory是否包含指定名稱的bean

  • ListableBeanFactory可羅列的BeanFactory,這裡的可羅列意味著此BeanFactory提供類似於<T> Map<String, T> getBeansOfType(@Nullable Class<T> type)這種獲取滿足條件的一系列bean的功能

  • AutowireCapableBeanFactory具備自動裝備功能的BeanFactory,提供了createBean範例化,屬性填充,回撥生命週期的建立bean,autowire自動裝配bean,initializeBean 回撥相關初始化方法,等方法

  • ConfigurableBeanFactory 可設定的BeanFactory,提供了setParentBeanFactory setBeanClassLoader,addBeanPostProcessor等設定bean工廠的方法

  • ConfigurableListableBeanFactory,實現了ListableBeanFactory,AutowireCapableBeanFactory,ConfigurableBeanFactory,具備其他三者的功能的同時,還提供了ignoreDependencyType 忽略給定依賴型別的自動裝配registerResolvableDependency 註冊指定型別的依賴使用指定的物件進行注入等功能

2.從DefaultListableBeanFactory 看註冊系介面

  • AliasRegistry:別名註冊,SimpleAliasRegistry是主要的實現,內部使用一個ConcurrentHashMap儲存bean名稱和別名

  • BeanDefinitionRegistry

    Bean定義註冊中心,主要是對BeanDefinition的增刪改查

  • SingletonBeanRegistry 單例bean註冊,提供註冊單例,獲取單例,等方法

四丶ApplicationContext類結構體系

1.ApplicationContext類結構體系分析

從類圖我們可以看到BeanFactoryApplicationContext的區別,總體來說ApplciationContext是一個BeanFactory但是包含其他更多的功能,這些功能就在它實現的其他介面體現

  • EnvironmentCapable提供獲取Environment的能力
  • ResourceLoader:用於載入資源(例如類路徑或檔案系統資源)的策略介面,根據路徑獲取資源包裝成Resource
  • ResourcePatternResolverResourceLoader的子介面,提供根據路徑匹配獲取資源的能力
  • ApplicationEventPublisher 事件傳送介面
  • MessageSource:用於解析訊息的策略介面,用於實現國際化

2.ClassPathXmlApplicationContext類結構體系分析

基於類路徑下xml檔案的Spring應用程式上下文,通過解析類路徑下的xml來載入Spring容器

  • ConfigurableApplicationContext可拱設定的Spring容器上下文,支援新增事件監聽器,設定父容器,設定環境,設定後置處理器等操作
  • AbstractApplicationContext 通過組合的方式,實現容器上下文的功能。提供了諸多模板方法,並且定義了Spring容器重新整理的基本邏輯,其中定義了refreshBeanFactory在spring容器重新整理的時候觸發BeanFactory的重新整理
  • AbstractRefreshableApplicationContext實現了refreshBeanFactory定義了重新整理BeanFactory的流程——建立BeanFactory,載入BeanDefinition(抽象方法交給子類實現)
  • AbstractRefreshableConfigApplicationContext實現了InitializingBeanafterPropertiesSet方法中呼叫容器重新整理的refresh方法
  • AbstractXmlApplicationContext抽象的xml上下文,對從xml載入BeanDefinition進行了實現,提供了getConfigResources方法讓子類自定義xml檔案的來源

3.AnnotationConfigApplicationContext類結構體系分析

基於註解包路徑掃描的容器上下文,內部持有AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner來完成BeanDefinition的註冊

  • GenericApplicationContext 內部持有一個DefaultListableBeanFactory,並且繼承了BeanDefinitionRegistry提供註冊BeanDefinition的方法,但是refresh方法不會去載入資源下的BeanDefinition,不像ClassPathXmlApplicationContext一樣會根據路徑載入BeanDefinition
  • AnnotationConfigRegistry,提供register(註冊一個或多個要處理的註解類)scan(在指定包路徑中執行掃描)方法

五丶ApplicationContext#refresh

ApplicationContext#refresh方法是Spring原始碼學習中最重要的方法,是Spring容器啟動會執行的方法,涉及到BeanDefinition的掃描,單例bean的生成等等。下面我們從AbstractApplicationContextrefresh方法簡單分析下,Spring容器啟動到底做了些什麼

1.重新整理BeanFactory

這部分會呼叫refreshBeanFactory(抽象方法交由子類實現),實現對BeanFactory的重新整理,對於AbstractRefreshableApplicationContext的子類會在其中銷燬原有的BeanFactory然後重寫建立BeanFactory,對於GenericApplicationContext子類什麼都不會做。

體現在ClassPathXmlApplicationContext會根據設定的xml路徑重新解析xml生成BeanDefinition並註冊,對於AnnotationConfigApplicationContext則是什麼都不做。

1.1 AbstractRefreshableApplicationContext#refreshBeanFactory 載入BeanDefinition

loadBeanDefinitions是一個抽象方法,具體如何載入BeanDefinition交給子類去實現,ClassPathXmlApplication載入BeanDefinition的流程在其父類別AbstractXmlApplicationContext中內部使用XmlBeanDefinitionReader#reader方法讀取Resouce生成BeanDefinition

1.2GenericApplicationContext的子類是如何載入BeanDefinition的

AnnotationConfigApplicationContext為例

其構造方法,如果傳入的是一個包路徑,那麼會呼叫無參構造方法,從而new 出AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner,然後使用ClassPathBeanDefinitionScanner掃描包下的所有具備@Component註解(包括複合註解)的類,如果傳入的是若干個類那麼AnnotatedBeanDefinitionReader會對這些類進行註冊。

2.prepareBeanFactory字首準備

這部分主要是對ConfigurableListableBeanFactory進行一些設定。其中會加入一個ApplicationContextAwareProcessorApplicationListenerDetectorBeanPostProcessor,並且忽略EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAwareMessageSourceAware,ApplicationContextAware等介面自動裝配,並且註冊BeanFactory,ResourceLoader,ApplicationEventPublisher,ApplicationContext的自動裝配值(依賴注入的時候,會使用註冊的值進行設定)

3.postProcessBeanFactory

留給子類擴充套件的方法,對於web應用上下文會在其中設定ServletContextAwareProcessorBeanPostProcessor,並且註冊request和 session的scope

4.invokeBeanFactoryPostProcessors 呼叫BeanFactoryPostProcessor

此方法會對BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor中的方法進行呼叫。呼叫通過ConfigurableApplicationContext#addBeanFactoryPostProcessor新增的,或者BeanFactoryBeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor型別的bean的bean名稱,getBean方法範例化後呼叫

4.1 什麼是BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor

  • BeanFactoryPostProcessor:Spring留給我們的一個擴充套件介面,在BeanDefinition載入註冊完之後,並執行一些前置操作之後會範例化所有的BeanFactoryPostProcessor範例並且回撥對應postProcessBeanFactory方法。允許自定義修改應用程式上下文的 bean 定義,調整上下文底層 bean 工廠的 bean 屬性值。應用程式上下文可以在其 bean 定義中自動檢測 BeanFactoryPostProcessor bean,並在建立任何其他 bean 之前應用它們。
  • BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子類,新增postProcessBeanDefinitionRegistry方法,在檢測``BeanFactoryPostProcessor型別的BeanDefinition之前就會呼叫所有BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry,這個方法允許我們新增,修改,刪除,查詢之前註冊的BeanDefinition,所有可以在這個方法中對BeanFactoryPostProcessor進行調整,也可以對其他所有BeanDefinition`進行調整

4.2 重要的BeanFactoryPostProcessor

4.2.1 PropertyPlaceholderConfigurer

它根據本地屬性,系統屬性和環境變數解析 ${...} 預留位置,會替換掉BeanDefinition中的預留位置。這也是基於xml設定資料來源的時候可以把資料庫設定放在一個檔案中,然後通過預留位置進行參照

4.2.2 EventListenerMethodProcessor

postProcessBeanFactory會範例化EventListenerFactory並且持有,並且它繼承了SmartInitializingSingletonafterSingletonsInstantiated方法中,會把每一個在方法中標註了@EventListener(或其複合註解)的bean和方法包裝成ApplicationListener註冊到Spring上下文(其實是委託給對於的事件多播器ApplicationEventMulticaster

為啥@Aysnc註解可以搭配@EventListener實現非同步的效果
afterSingletonsInstantiated呼叫實際是所有單例bean初始化後
這時候bean已近是被@Aysnc對於的BeanPostProcessor進行CGLIB增強的bean了
呼叫方法便以及是非同步呼叫的了

其實spring的預設的多播器SimpleApplicationEventMulticaster,內部持有一個JUC的Executor,可以設定成執行緒池,那麼就是不需要@Aysnc也能非同步呼叫
4.2.3ConfigurationClassPostProcessor

解析加了@Configuration的設定類,還會解析@ComponentScan@ComponentScans註解掃描的包,以及解析@Bean,@Import,@PropertySources,@ImportResource等註解,將這些註解資訊解析成BeanDefinition註冊到Spring BeanFactory中。

4.2.4 工作中碰到的用法

其實和PropertyPlaceholderConfigurer差不多,公司為了避免組態檔中明文儲存資料庫密碼,採用一種加密方式組態檔儲存加密後的密文,然後用SpringBoot的自動設定注入一個BeanFactoryPostProcessor進行解密。

5.registerBeanPostProcessors 註冊BeanPostProcessor

5.1 什麼是BeanPostProcessor

一個允許自定義修改新 bean 範例的介面,作用是在Bean物件在範例化和依賴注入完畢後,在顯示呼叫初始化方法的前後新增我們自己的邏輯。注意是Bean範例化完畢後及依賴注入完成後觸發的

5.2 registerBeanPostProcessors的邏輯

獲取BeanFactory中的BeanPostProcessor型別的bean,根據實現PriorityOrdered中的getOrder方法順序>實現Ordered中getOrder方法的順序>@Order標註的順序>沒有實現這兩個介面也沒有標註註解的順序(是否支援@Order註解排序,取決於BeanFactory使用的比較器)

5.3 BeanPostProcessor 類結構體系

5.3.1 BeanPostProcessor

提供兩個方法:postProcessBeforeInitialization(在bean初始化方法呼叫之前被Spring容器呼叫),postProcessAfterInitialization(在bean初始化方法呼叫之後呼叫)

5.3.2 MergedBeanDefinitionPostProcessor

在BeanPostProcessor的基礎上新增一個方法postProcessMergedBeanDefinition在建立bean時,會先獲取bean對應的BeanDefinition(Spring可能對存在父子關係的beanDefinition進行合併)後,spring會範例化bean然後會呼叫postProcessMergedBeanDefinition,入參中有BeanDefinition可以進行自定義的修改

5.3.3 InstantiationAwareBeanPostProcessor

在BeanFactoryPostProcessor的基礎新增postProcessBeforeInstantiation(在Spring範例化bean之前呼叫,返回非空物件可以阻斷Spring後續範例化,屬性填充,初始化方法的呼叫咯流程),postProcessAfterInstantiation(在範例化 bean 之後,但在 Spring 屬性填充發生執行操作),postProcessProperties在spring將給定的屬性值應用到給定的 bean 後呼叫

5.3.4 DestructionAwareBeanPostProcessor

在BeanFactoryPostProcessor的基礎新增postProcessBeforeDestruction在bean被銷燬之前呼叫

5.3.5 SmartInstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor的基礎新增determineCandidateConstructors(Spring會推斷構造方法來反射生成物件,這個方法可以決定使用哪些構造方法)getEarlyBeanReference(獲取對指定 bean 的早期暴露的參照,如果出現迴圈依賴,獲取物件的時候會呼叫此)

5.4 Spring原始碼中BeanPostProcessor的應用

5.4.1 ApplicationContextAwareProcessor

負責EnvironmentAware(環境感知介面),EmbeddedValueResolverAware(可以拿到上下文中解析字串值的簡單策略介面實現)ResourceLoaderAware(可以拿到上下文中用於載入資源(例如類路徑或檔案系統資源)的策略介面實現)ApplicationEventPublisherAware(可以拿到上下文中事件釋出功能的介面實現)MessageSourceAware(可以拿到上下文解析訊息的策略介面的實現)ApplicationContextAware(spring上下文感知介面)介面的回撥,回撥對於的set方法

5.4.2 AnnotationAwareAspectJAutoProxyCreator

Spring 原始碼學習筆記10——Spring AOP提到過,AnnotationAwareAspectJAutoProxyCreator負責解析AspeJ註解標註類,排序然後包裝成Advisor,最後對判斷bean是否需要代理,最後由ProxyFactory獲取AopProxy進行JDK動態代理或者CGLIB動態代理,生成代理物件,這就是Spring 基於註解的AOP原理

5.4.3 AutowiredAnnotationBeanPostProcessor

Spring原始碼學習筆記7——Spring bean的初始化,和 Spring原始碼學習筆記9——構造器注入及其迴圈依賴 中學過,其determineCandidateConstructors會根據@Autowired or @Value推斷構造方法,如果只有一個建構函式那麼模式使用此,這也就是為什麼,提供一個建構函式,spring會從容器中拿到複合引數型別bean進行構造,也是為什麼@Autowired or @Value標註在構造方法會生效。其postProcessProperties會進行屬性注入(利用欄位反射,或者set方法反射),其postProcessMergedBeanDefinition方法先於另外兩個方法執行,它會掃描bean class中的所有方法和欄位,如果具備@Autowired or @Value註解 那麼會包裝成InjectionMetadata並快取,後續在determineCandidateConstructorspostProcessProperties中發揮作用

5.4.4 CommonAnnotationBeanPostProcessor

Spring原始碼學習筆記7——Spring bean的初始化 學習過,負責解析@PostConstruct,@PreDestroy,@Resource,在postProcessMergedBeanDefinition中會把@PostConstruct,@PreDestroy標註的方法包裝成LifecycleMetadata,其中PostConstruct標註的放法會在postProcessBeforeInitialization(範例化之後,初始化之前)中被呼叫,PreDestroy標註的方法在postProcessBeforeDestruction bean被消耗之前呼叫。@Resource@Autowired類似,都是在postProcessProperties中呼叫,但是獲取bean的策略有所不同,可以看Spring原始碼學習筆記7——Spring bean的初始化中對二者進行的對比

5.4.5 ApplicationListenerDetector

ApplicationListenerDetector 監聽器探測器,它會在ApplicationListener型別的bean被範例化之後,註冊到上下文的事件多播器中。在ApplicationListener型別的bean被銷燬之前,從上下文持有的事件多播器中刪除

5.4.6 AsyncAnnotationBeanPostProcessor

使用AsyncAnnotationAdvisor對bean進行增強,如果方法上具備@Async註解,會呼叫AnnotationAsyncExecutionInterceptor中的invoke進行增強,實現非同步呼叫,也就是基於Spring AOP的實現。

5.4.7 ScheduledAnnotationBeanPostProcessor

會掃描每一個bean的方法,如果上面標註了@Scheduled,@Schedules註解那麼會被註冊到TaskScheduler,基於Juc中的定時任務執行緒池原理定時執行。

6.initApplicationEventMulticaster 初始化事件多播器

會從容器中,獲取名稱為applicationEventMulticaster的bean,如果存在那麼Spring容器將持有此事件多播器,監聽器的註冊,取消註冊,事件的推播都依賴此事件多播器,如果沒有對應的bean那麼使用預設的SimpleApplicationEventMulticaster

SimpleApplicationEventMulticaster:如果設定了Executor那麼響應事件會呼叫Executor#execute具體非同步還是同步,取決於Executor的內部實現(這就是JUC原始碼學習筆記5——執行緒池,FutureTask,Executor框架原始碼解析中提到的Executor的作用——把任務提交與每個任務將如何執行的機制進行解耦),如果沒有那麼就由傳送事件的執行緒進行呼叫(同步)

7.onRefresh勾點方法

SpringBoot使用的AnnotationConfigServletWebServerApplicationContext在這個方法裡面開啟Tomcate伺服器

8.registerListeners註冊監聽器

負責把事件監聽器 註冊到多播器中,並且在事件多播器初始化執行之前,可能存在一些事件沒有處理,這些事件都會放在earlyApplicationEvents集合中,在多播器初始化後,會把earlyApplicationEvents置為null 以後的事件都是直接委託給多播器進行推播。

9.finishBeanFactoryInitialization範例化所有非懶載入的單例bean

這個方法貫穿了bean的範例化,屬性填充,初始化。

Spring原始碼學習筆記6——Spring bean的範例化

Spring原始碼學習筆記7——Spring bean的初始化

Spring原始碼學習筆記8——Spring是如何解決迴圈依賴的

Spring原始碼學習筆記9——構造器注入及其迴圈依賴

這四篇筆記中詳細的說明了Bean的初始化流程,下面我進行粗略的總結,提前初始化單例bean的方法是DefaultListableBeanFactory#preInstantiateSingletons

DefaultListableBeanFactory#preInstantiateSingletons存在兩個for迴圈,第一個是範例化載入所有的單例非懶載入bean,第二個for迴圈是在載入完後,呼叫實現了SmartInitializingSingleton介面bean的afterSingletonsInstantiated方法,這是spring留給我們的一個擴充套件介面

9.1.什麼樣的bean才會被範例化

首先要滿足!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit(),即不是抽象,且單例,且不是懶載入的BeanDefinition

9.2 FactoryBean和普通bean的不同處理

  • 什麼是FactoryBean

    FactoryBean是spring留給我們創造複雜物件的一個介面,可以把它看作是製造bean的一個工廠,Spring如果發現BeanDefinition中的bean型別是FactoryBean那麼會呼叫FactoryBean#getObject方法產生bean。

  • 建立FactoryBean 和 其對應的bean

    首先如果是FactoryBean 那麼bean名稱前面會有FactoryBean專屬的字首&,Spring會先初始化FactoryBean然後如果是SmartFactoryBean並且渴望初始化那麼會呼叫getBean,這個方法發現製造的bean對應的BeanDefinition記錄的型別是FactoryBean那麼會呼叫FactoryBean的getObject方法。而普通bean直接呼叫getBean方法建立,也就是說FactoryBean型別的bean,如果不是SmartFactoryBean且渴望載入,那麼Spring容器啟動之後載入FactoryBean——有點懶載入的意思。

9.3 Spring建立bean

上面我們看到初始化所有懶載入單例bean呼叫的是BeanFactory#getBean(beanName),這裡的BeanFactory一般是DefaultSingletonBeanRegistry其內部使用map組成三級快取,並且還要對factoryBan的快取,如下圖

下面我們看下建立bean的邏輯

9.3.1前置邏輯 factoryBean和父beanFactory相關

首先是一級快取即單例池中獲取,如果之前之前建立過單例物件,那麼會被快取到一級快取單例池中,這時候就直接拿到了,但是拿到的可能是FactoryBean,所有呼叫getObjectForBeanInstance方法,如果是FactoryBean那麼會呼叫其getObject生成bean。如果當前BeanFactory沒有此beanName對應的BeanDefinition那麼會呼叫父beanFactroy的getBean方法。反之說明存在對應的beanDefinition這時候就是取建立bean的流程了

9.3.2 範例化bean——createBeanInstance方法
9.3.2.1 範例化單例物件

建立bean之前,首先會獲取當前beanDefinition中依賴的bean,這個可以通過@DependOn註解或者xml標籤指定,spring會先去載入這些bean(同樣是使用getBean(bean名稱)),然後再來範例化當前bean。

範例化之前首先會從一級快取中拿,如果由那麼直接返回,然後呼叫InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation如果返回了一個非null物件那麼會呼叫BeanPostProcessor#postProcessAfterInitialization然後返回,也就是說InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation允許我們阻斷spring範例化,依賴注入,初始化bean的流程,使用這個介面產生的bean,如果產生了那麼呼叫BeanPostProcessor#postProcessAfterInitialization這意味著bean以及初始化完了(注意是初始化,也就是說明postProcessBeforeInstantiation需要我們自己範例化物件,初始化物件。並且意味著SpringAop是繼續生效的)。

其次我們可以通過在beanDefinition中指定instanceSupplier來自定義spring bean,然後直接返回。或者如果我們指定了由工廠方法生成,那麼會解析工廠方法入參,從容器中拿到對應的bean然後呼叫對應的工廠方法,生成物件,然後直接返回。這兩種方式都不會讓aop生效,因為BeanPostProcessor沒有被回撥。

再者呼叫SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors,來決定使用哪個建構函式,如果存在優先選擇public且引數最多的建構函式進行構造。這裡就是AutowiredAnnotationBeanPostProcessor怎麼決定建構函式的,它會優先選擇由@Autowired or @Value註解的,如果只有一個那麼直接使用唯一的一個建構函式,其餘情況返回null,後續Spring自己使用建構函式,還是使用CGLIB生成子類的策略範例化bean。

9.3.2.2 範例化原型物件

建立原型物件和單例物件類似,但是原型物件是不允許迴圈依賴的,beanFactory使用prototypesCurrentlyInCreationThreadLocal儲存了原型物件是否處於建立中,如果傳送回圈依賴那麼將丟擲異常

9.3.2.3範例化特殊作用域的bean
這裡說是範例化,其實是範例化+屬性填充+初始化
之所以說範例化,是因為單例,原型,特殊作用域的bean只是範例化觸發的條件不同
單例只範例化一次
原型每次getBean都範例化
特殊作用域取決於作用域的邏輯

我們重點關注下Scope#get方法,它是如何控制作用域的,此方法的含義是先嚐試在Scope中拿(一般是一個快取,Map或者ThreadLocal)如果沒有那麼呼叫後續傳入的lambda。

  • SessionScope 和 RequestScope

    session 和request 範圍

    SessionScope 和 RequestScope 都是基於RequestContextHolder(內部使用requestAttributesHolder的ThreadLocal儲存請求資訊,在FrameworkServlet也就是DispatcherServlet的父類別中,每當一個請求存取時就把請求資訊儲存到ThreadLocal中,也就是說,session和request的作用域,必須要求請求時通過DispatcherServlet的)實現的,不同的是SessionScope#get會把Session物件作為互斥鎖(也許是因為同一個session可以並行發生請求,這時候需要獲取互斥鎖,避免相同session不同bean範例),RequestScope#get則不會,二者控制的bean範例都是儲存在ThreadLocal中的RequestAttributes物件中,RequestAttributes的子類ServletRequestAttributes會根據Scope的不同選擇把bean存在request中還是session中從而實現作用域的控制。

  • ServletContextScope

它是基於servletContext實現的,一個應用只有一個servletContext

  • SimpleThreadScope

它是基於ThreadLocal實現的

  • SimpleTransactionScope

基於TransactionSynchronizationManager實現的,TransactionSynchronizationManager內部基於ThreadLocal,和SimpleThreadScope不同的是,Spring建立的邏輯事務,即使在同一執行緒上,如果是一個獨立的事務,spring會先解綁當前執行緒上面的資訊,掛起當前事務,從而導致兩個邏輯事務使用到的bean是不同的bean

什麼樣的,事務被視作時一個獨立事務暱

如果外部不存在一個事務,並且傳播級別是REQUIRED,REQUIRES_NEW,NESTED
如果外部存在一個事務,且傳播級別為REQUIRES_NEW
如果外部存在一個事務,且傳播級別為巢狀事務,但是此時不是通過儲存點來實現巢狀事務

上面三個條件也意味著內部事務和外部事務拿到的bean是不同的
9.3.3 屬性填充populateBean
9.3.3.1填充之前
9.3.3.2 屬性注入的原理
  • @Resource,@Autowired,@Value實現的原理

    Spring原始碼學習筆記7——Spring bean的初始化在這篇中我們詳細說明了@Resource,@Autowired,@Value註解的實現原理。

    @Autowired和@Value依賴於AutowiredAnnotationBeanPostProcessor這個後置處理器在其postProcessMergedBeanDefinition方法中,它會掃描所有bean的欄位和方法(父類別也會掃描到)將需要進行依賴注入的欄位和方法包裝成InjectionMetadata,後續在postProcessProperties方法中,會迴圈遍歷需要注入的欄位和方法,首先會找到合適的注入bean,然後反射進行設定。找到合適的bean依賴的是DefaultListableBeanFactory#resolveDependency方法

    對於Optional型別的依賴向,Spring在呼叫doResolveDependency後進行包裝。對於ObjectFactory,ObjectProvider型別的依賴項,Spring包裝成二者的子類DependencyObjectProvider呼叫對應方法的時候還是依賴doResolveDependency方法找到合適的bean。然後是處理@Lazy標註的欄位或者方法引數,如果存在註解那麼spring會使用ProxyFactory生成代理類,代理類使用了自定義的TargetSource在使用的時候才會呼叫doResolveDependency獲取依賴的bean物件。如果沒有lazy註解那麼直接呼叫doResolveDependency方法返回依賴項。

    首先是呼叫getSuggestedValue方法解析@value註解並且進行解析器解析(這就是為什麼@Value(環境中的一個key,甚至spel表示式) 可以生效的原型),然後使用型別轉換器進行轉換(比如組態檔中是字串但是欄位是int,這時候會進行轉換),然後是resolveMultipleBeans方法,它負責解析Stream,陣列,Collection,Map型別的,其中Stream,陣列,Collection型別會從容器中拿到合適的bean組成流,陣列或者集合返回,Map型別則會返回key為bean名稱,value為bean的map。找到合適的bean都是呼叫的findAutowireCandidates方法,這個方法首先根據注入的型別拿到所有的符合的bean名稱,然後還會加上在resolvableDependencies這個Map中存在符合條件的(ApplicationContext這種不在beanFactory中的bean就是通過這個方法找到的)然後判斷bean是否是候選bean,首先要求當前這個候選者對應的bean定義中isAutowireCandidate方法返回true(這就是為什麼@Bean註解注入的物件不會被視作依賴注入的候選者)然後判斷是否具備@Qualifier註解,如果存在那麼進行進一步的篩選。然後如果存在多個滿足條件的候選者,首先判斷BeanDefinition#isPrimary是否是true(如果存在多個那麼本BeanFactory的候選者優先於父BeanFactory,如果還是存在多個丟擲異常NoUniqueBeanDefinitionException)然後比較 javax.annotation.Priority註解中標註的優先順序,取最大者,存在多個依舊是NoUniqueBeanDefinitionException,最後resolvableDependenciesmap中的候選者,優先於其他候選者。

  • PropertyValues屬性注入原理

    BeanDefinition.getPropertyValues()這個方法返回bean的屬性需要設定的值,一般在xml設定bean的時候比較常用,但是也被用於類似於Feign掃描介面構建FactoryBean的時候指定FactoryBean的屬性值。我們實際開發中用得比較少.

    上圖中的例子就是通過BeanDefinitionRegistryPostProcessor自定義註冊BeanDefinition,並且指定了屬性需要注入生麼值,其中str會被注入字串straaaa,屬性b是RuntimeBeanReference會被注入名稱為b的bean物件。負責這些屬性注入的方法是applyPropertyValues。最終反射設定值呼叫的是BeanWrapper#setPropertyValues方法,Spring會先進行型別轉換,然後反射(一般是呼叫對應的set方法)

9.3.4 bean初始化initializeBean

上面我們學習了Spring是如何範例化bean,並且進行依賴注入的,但是完成這些步驟的bean還沒有執行初始化,比如說標記的初始化方法還沒有被回撥,接下來我們看下是spring是如何實現的

9.3.4.1 invokeAwareMethods回撥感知介面

這裡就是判斷是否實現了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware會呼叫對應的set方法

這裡可以看到並沒有呼叫ApplicationContext因為這是在DefaultListableBeanFactory中實現的操作,DefaultListableBeanFactory並不知道ApplicationContext的存在

9.3.4.2初始化之前的後置處理器回撥

就是呼叫所有的BeanPostProcessor#postProcessBeforeInitialization這裡便會呼叫到

ApplicationContextAwareProcessor,它會負責回撥EnvironmentAware(環境感知介面),EmbeddedValueResolverAware(可以拿到上下文中解析字串值的簡單策略介面實現)ResourceLoaderAware(可以拿到上下文中用於載入資源(例如類路徑或檔案系統資源)的策略介面實現)ApplicationEventPublisherAware(可以拿到上下文中事件釋出功能的介面實現)MessageSourceAware(可以拿到上下文解析訊息的策略介面的實現)ApplicationContextAware(spring上下文感知介面)對應的方法

以及InitDestroyAnnotationBeanPostProcessor(CommonAnnotationBeanPostProcessor的子類,其實是呼叫到CommonAnnotationBeanPostProcessor)的postProcessBeforeInitialization方法,他會呼叫@PostConstruct標註的方法。(並且如果容器銷燬bean還會呼叫@PreDestroy標註的方法)

9.3.4.3初始化方法回撥

首先執行InitializingBean#afterPropertiesSet方法,然後指定自定義在BeanDefinition中的初始化方法。這裡可以看到InitializingBeanSmartInitializingSingleton的區別,前者是每一個bean的初始化的時候呼叫,後者是所有單例bean預載入之後呼叫。如果在InitializingBean中使用容器取getBean那麼可能會觸發其他bean的載入,SmartInitializingSingleton則不會造成,因為呼叫的時候已經預載入了所有的非懶載入的單例bean

9.3.4.5 初始化之後的後置處理器回撥

呼叫BeanPostProcessor#postProcessAfterInitialization這裡便是發生動態代理AOP增強的地方(Spring 原始碼學習筆記10——Spring AOP)

10.finishRefresh

呼叫LifecycleProcessor#onRefresh方法,推播ContextRefreshedEvent的事件