Spring源碼學習筆記12——總結篇,IOC,Bean的生命周期,三大擴展點

Spring源碼學習筆記12——總結篇,IOC,Bean的生命周期,三大擴展點

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

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

系列文章目錄和關於我

一丶什麼是 IOC 和DI

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

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

  • IoC是如何描述依賴的

    • 構造方法的參數
    • 產生對象的工廠方法的參數
    • 對象設置的屬性

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

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

img

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

img

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

image-20220831234125460

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的作用域,是否懶載入等等。

image-20220831225404839

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

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

3.BeanDefinitionReader

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

image-20220831231718629

image-20220831231332892

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

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

4.BeanDefinitionRegistry

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

image-20220831233347368

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

三丶BeanFactory類結構體系

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

1.從DefaultListableBeanFactory看BeanFactory類結構體系

image-20220902113202341

  • 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 看註冊系介面

image-20220902152044859

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

  • BeanDefinitionRegistry

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

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

四丶ApplicationContext類結構體系

1.ApplicationContext類結構體系分析

image-20220902162646693

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

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

2.ClassPathXmlApplicationContext類結構體系分析

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

image-20220902170327305

  • 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的註冊

image-20220903143433330

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

五丶ApplicationContext#refresh

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

image-20220904120106897

1.刷新BeanFactory

這部分會調用refreshBeanFactory(抽象方法交由子類實現),實現對BeanFactory的刷新,對於AbstractRefreshableApplicationContext的子類會在其中銷毀原有的BeanFactory然後重寫創建BeanFactory,對於GenericApplicationContext子類什麼都不會做。

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

1.1 AbstractRefreshableApplicationContext#refreshBeanFactory 載入BeanDefinition

image-20220904122553218

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

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

AnnotationConfigApplicationContext為例

image-20220904123123295

其構造方法,如果傳入的是一個包路徑,那麼會調用無參構造方法,從而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

image-20220904145627228

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 類結構體系

image-20220904155438697

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 的早期暴露的引用,如果出現循環依賴,獲取對象的時候會調用此)

image-20220905214215025

5.4 Spring源碼中BeanPostProcessor的應用

image-20220904171059821

5.4.1 ApplicationContextAwareProcessor

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

image-20220904164755601

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註冊監聽器

img

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

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

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

Spring源碼學習筆記6——Spring bean的實例化

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

Spring源碼學習筆記8——Spring是如何解決循環依賴的

Spring源碼學習筆記9——構造器注入及其循環依賴

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

image-20220905124150686

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。

    image-20220905194945547

  • 創建FactoryBean 和 其對應的bean

    image-20220905195327272

    首先如果是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的快取,如下圖

image-20220905210635334

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

9.3.1前置邏輯 factoryBean和父beanFactory相關

image-20220905203849973

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

9.3.2 實例化bean——createBeanInstance方法
9.3.2.1 實例化單例對象

image-20220905214550682

創建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都實例化
特殊作用域取決於作用域的邏輯

image-20220906175145293

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

image-20220906175926933

  • 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方法

    image-20220906160919791

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

    image-20220906163351726

    首先是調用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的屬性值。我們實際開發中用得比較少.

    image-20220906170850424

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

9.3.4 bean初始化initializeBean

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

image-20220906172446714

9.3.4.1 invokeAwareMethods回調感知介面

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

image-20220906172835412

這裡可以看到並沒有調用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

image-20220906173300584

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

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

10.finishRefresh

調用LifecycleProcessor#onRefresh方法,推送ContextRefreshedEvent的事件

Tags: