參考了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,物件在被構造方法構造,或者工廠方法創造返回,僅通過建構函式引數、工廠方法的引數,或者物件設定的屬性來定義他們的關係(即與它們一起工作的其他物件)。
什麼叫依賴——物件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是指Spring 容器管理的物件,一個Spring 容器管理多個Spring bean,對它們進行範例化,依賴注入,初始化等。這些 bean 是使用我們提供給spring容器的設定後設資料建立的(例如,以 XML <bean/>
定義的形式)。
在容器中,這些 bean 定義表示為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進行管理。
Bean定義資訊讀取器,定義瞭如下方法
BeanDefinitionReader
的典型實現莫過於XmlBeanDefinitionReader
,它負責解析xml生成BeanDefinition並註冊(spring原始碼學習筆記1——解析xml生成BeanDefinition的過程解析)
這個類關係圖中有兩個異類AnnotatedBeanDefinitionReader
,ClassPathBeanDefinitionScanner
,它們沒有實現BeanDefinitionReader
但是存在的目的和BeanDefinitionReader
是一致的——將生成BeanDefinition並註冊到容,並且根據設定生成beanDefinition的動作交給他們,並不是直接讓BeanFactory來完成,更加可延伸,更加單一職責。
Bean定義註冊中心,主要是對BeanDefinition
的增刪改查,自然內部會對BeanDefinition
資訊進行儲存。
DefaultListableBeanFactory
是一個Bean工廠,Spring中很多上下文都是通過組合它來實現對Bean的管理。其中還涉及到AliasRegistry
顧名思義就是對bean的別名進行管理。
Spring 容器的根介面,主要提供了getBean
(根據bean名稱,型別等獲取bean的方法),以及判斷bean是否單例,是否原型等方法。Spring建議BeanFactory
的實現類儘可能的支援bean的生命週期介面的回撥(比如InitializingBean#afterPropertiesSet
等)其中最關鍵的實現類莫過於DefaultListableBeanFactory
。
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 註冊指定型別的依賴使用指定的物件進行注入
等功能
AliasRegistry
:別名註冊,SimpleAliasRegistry
是主要的實現,內部使用一個ConcurrentHashMap
儲存bean名稱和別名
BeanDefinitionRegistry
Bean定義註冊中心,主要是對BeanDefinition
的增刪改查
SingletonBeanRegistry
單例bean註冊,提供註冊單例,獲取單例,等方法
從類圖我們可以看到BeanFactory
和ApplicationContext
的區別,總體來說ApplciationContext
是一個BeanFactory
但是包含其他更多的功能,這些功能就在它實現的其他介面體現
EnvironmentCapable
提供獲取Environment
的能力ResourceLoader
:用於載入資源(例如類路徑或檔案系統資源)的策略介面,根據路徑獲取資源包裝成Resource
ResourcePatternResolver
:ResourceLoader
的子介面,提供根據路徑匹配獲取資源的能力ApplicationEventPublisher
事件傳送介面MessageSource
:用於解析訊息的策略介面,用於實現國際化基於類路徑下xml檔案的Spring應用程式上下文,通過解析類路徑下的xml來載入Spring容器
ConfigurableApplicationContext
可拱設定的Spring容器上下文,支援新增事件監聽器,設定父容器,設定環境,設定後置處理器等操作AbstractApplicationContext
通過組合的方式,實現容器上下文的功能。提供了諸多模板方法,並且定義了Spring容器重新整理的基本邏輯,其中定義了refreshBeanFactory
在spring容器重新整理的時候觸發BeanFactory
的重新整理AbstractRefreshableApplicationContext
實現了refreshBeanFactory
定義了重新整理BeanFactory
的流程——建立BeanFactory
,載入BeanDefinition
(抽象方法交給子類實現)AbstractRefreshableConfigApplicationContext
實現了InitializingBean
在afterPropertiesSet
方法中呼叫容器重新整理的refresh
方法AbstractXmlApplicationContext
抽象的xml上下文,對從xml載入BeanDefinition
進行了實現,提供了getConfigResources
方法讓子類自定義xml檔案的來源基於註解包路徑掃描的容器上下文,內部持有AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner
來完成BeanDefinition
的註冊
GenericApplicationContext
內部持有一個DefaultListableBeanFactory
,並且繼承了BeanDefinitionRegistry
提供註冊BeanDefinition
的方法,但是refresh
方法不會去載入資源下的BeanDefinition
,不像ClassPathXmlApplicationContext
一樣會根據路徑載入BeanDefinition
AnnotationConfigRegistry
,提供register(註冊一個或多個要處理的註解類)
和scan(在指定包路徑中執行掃描)
方法ApplicationContext#refresh
方法是Spring原始碼學習中最重要的方法,是Spring容器啟動會執行的方法,涉及到BeanDefinition的掃描,單例bean的生成等等。下面我們從AbstractApplicationContext
對refresh
方法簡單分析下,Spring容器啟動到底做了些什麼
這部分會呼叫refreshBeanFactory
(抽象方法交由子類實現),實現對BeanFactory的重新整理,對於AbstractRefreshableApplicationContext
的子類會在其中銷燬原有的BeanFactory然後重寫建立BeanFactory,對於GenericApplicationContext
子類什麼都不會做。
體現在ClassPathXmlApplicationContext
會根據設定的xml路徑重新解析xml生成BeanDefinition並註冊,對於AnnotationConfigApplicationContext
則是什麼都不做。
loadBeanDefinitions
是一個抽象方法,具體如何載入BeanDefinition
交給子類去實現,ClassPathXmlApplication
載入BeanDefinition的流程在其父類別AbstractXmlApplicationContext
中內部使用XmlBeanDefinitionReader#reader
方法讀取Resouce
生成BeanDefinition
以AnnotationConfigApplicationContext
為例
其構造方法,如果傳入的是一個包路徑,那麼會呼叫無參構造方法,從而new 出AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
,然後使用ClassPathBeanDefinitionScanner
掃描包下的所有具備@Component
註解(包括複合註解)的類,如果傳入的是若干個類那麼AnnotatedBeanDefinitionReader
會對這些類進行註冊。
這部分主要是對ConfigurableListableBeanFactory
進行一些設定。其中會加入一個ApplicationContextAwareProcessor
和ApplicationListenerDetector
的BeanPostProcessor
,並且忽略EnvironmentAware
,EmbeddedValueResolverAware
,ResourceLoaderAware
,ApplicationEventPublisherAware
,MessageSourceAware
,ApplicationContextAware
等介面自動裝配,並且註冊BeanFactory
,ResourceLoader
,ApplicationEventPublisher
,ApplicationContext
的自動裝配值(依賴注入的時候,會使用註冊的值進行設定)
留給子類擴充套件的方法,對於web應用上下文會在其中設定ServletContextAwareProcessor
的BeanPostProcessor
,並且註冊request和 session的scope
此方法會對BeanDefinitionRegistryPostProcessor
和BeanFactoryPostProcessor
中的方法進行呼叫。呼叫通過ConfigurableApplicationContext#addBeanFactoryPostProcessor
新增的,或者BeanFactory
中BeanDefinitionRegistryPostProcessor
和BeanFactoryPostProcessor
型別的bean的bean名稱,getBean
方法範例化後呼叫
BeanFactoryPostProcessor
:Spring留給我們的一個擴充套件介面,在BeanDefinition
載入註冊完之後,並執行一些前置操作之後會範例化所有的BeanFactoryPostProcessor
範例並且回撥對應postProcessBeanFactory
方法。允許自定義修改應用程式上下文的 bean 定義,調整上下文底層 bean 工廠的 bean 屬性值。應用程式上下文可以在其 bean 定義中自動檢測 BeanFactoryPostProcessor bean,並在建立任何其他 bean 之前應用它們。BeanDefinitionRegistryPostProcessor
:BeanFactoryPostProcessor
的子類,新增postProcessBeanDefinitionRegistry
方法,在檢測``BeanFactoryPostProcessor型別的
BeanDefinition之前就會呼叫所有
BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry,這個方法允許我們新增,修改,刪除,查詢之前註冊的
BeanDefinition,所有可以在這個方法中對
BeanFactoryPostProcessor進行調整,也可以對其他所有
BeanDefinition`進行調整PropertyPlaceholderConfigurer
它根據本地屬性,系統屬性和環境變數解析 ${...}
預留位置,會替換掉BeanDefinition
中的預留位置。這也是基於xml設定資料來源的時候可以把資料庫設定放在一個檔案中,然後通過預留位置進行參照
EventListenerMethodProcessor
其postProcessBeanFactory
會範例化EventListenerFactory
並且持有,並且它繼承了SmartInitializingSingleton
在afterSingletonsInstantiated
方法中,會把每一個在方法中標註了@EventListener
(或其複合註解)的bean和方法包裝成ApplicationListener
註冊到Spring上下文(其實是委託給對於的事件多播器ApplicationEventMulticaster
)
為啥@Aysnc註解可以搭配@EventListener實現非同步的效果
afterSingletonsInstantiated呼叫實際是所有單例bean初始化後
這時候bean已近是被@Aysnc對於的BeanPostProcessor進行CGLIB增強的bean了
呼叫方法便以及是非同步呼叫的了
其實spring的預設的多播器SimpleApplicationEventMulticaster,內部持有一個JUC的Executor,可以設定成執行緒池,那麼就是不需要@Aysnc也能非同步呼叫
ConfigurationClassPostProcessor
解析加了@Configuration
的設定類,還會解析@ComponentScan
、@ComponentScans
註解掃描的包,以及解析@Bean,@Import,@PropertySources,@ImportResource
等註解,將這些註解資訊解析成BeanDefinition
註冊到Spring BeanFactory中。
其實和PropertyPlaceholderConfigurer
差不多,公司為了避免組態檔中明文儲存資料庫密碼,採用一種加密方式組態檔儲存加密後的密文,然後用SpringBoot的自動設定注入一個BeanFactoryPostProcessor進行解密。
一個允許自定義修改新 bean 範例的介面,作用是在Bean物件在範例化和依賴注入完畢後,在顯示呼叫初始化方法的前後新增我們自己的邏輯。注意是Bean範例化完畢後及依賴注入完成後觸發的
獲取BeanFactory中的BeanPostProcessor型別的bean,根據實現PriorityOrdered中的getOrder方法順序
>實現Ordered中getOrder方法的順序
>@Order標註的順序
>沒有實現這兩個介面也沒有標註註解的順序
(是否支援@Order
註解排序,取決於BeanFactory使用的比較器)
提供兩個方法:postProcessBeforeInitialization(在bean初始化方法呼叫之前被Spring容器呼叫)
,postProcessAfterInitialization(在bean初始化方法呼叫之後呼叫)
在BeanPostProcessor的基礎上新增一個方法postProcessMergedBeanDefinition
在建立bean時,會先獲取bean對應的BeanDefinition(Spring可能對存在父子關係的beanDefinition進行合併)後,spring會範例化bean然後會呼叫postProcessMergedBeanDefinition
,入參中有BeanDefinition
可以進行自定義的修改
在BeanFactoryPostProcessor的基礎新增postProcessBeforeInstantiation(在Spring範例化bean之前呼叫,返回非空物件可以阻斷Spring後續範例化,屬性填充,初始化方法的呼叫咯流程)
,postProcessAfterInstantiation(在範例化 bean 之後,但在 Spring 屬性填充發生執行操作)
,postProcessProperties在spring將給定的屬性值應用到給定的 bean 後呼叫
在BeanFactoryPostProcessor的基礎新增postProcessBeforeDestruction在bean被銷燬之前呼叫
。
在InstantiationAwareBeanPostProcessor
的基礎新增determineCandidateConstructors(Spring會推斷構造方法來反射生成物件,這個方法可以決定使用哪些構造方法)
,getEarlyBeanReference(獲取對指定 bean 的早期暴露的參照,如果出現迴圈依賴,獲取物件的時候會呼叫此)
負責EnvironmentAware(環境感知介面)
,EmbeddedValueResolverAware(可以拿到上下文中解析字串值的簡單策略介面實現)
,ResourceLoaderAware(可以拿到上下文中用於載入資源(例如類路徑或檔案系統資源)的策略介面實現)
,ApplicationEventPublisherAware(可以拿到上下文中事件釋出功能的介面實現)
,MessageSourceAware(可以拿到上下文解析訊息的策略介面的實現)
,ApplicationContextAware(spring上下文感知介面)
介面的回撥,回撥對於的set方法
在Spring 原始碼學習筆記10——Spring AOP提到過,AnnotationAwareAspectJAutoProxyCreator負責解析AspeJ註解標註類,排序然後包裝成Advisor
,最後對判斷bean是否需要代理,最後由ProxyFactory獲取AopProxy進行JDK動態代理或者CGLIB動態代理,生成代理物件,這就是Spring 基於註解的AOP原理
在Spring原始碼學習筆記7——Spring bean的初始化,和 Spring原始碼學習筆記9——構造器注入及其迴圈依賴 中學過,其determineCandidateConstructors
會根據@Autowired or @Value
推斷構造方法,如果只有一個建構函式那麼模式使用此,這也就是為什麼,提供一個建構函式,spring會從容器中拿到複合引數型別bean進行構造,也是為什麼@Autowired or @Value
標註在構造方法會生效。其postProcessProperties
會進行屬性注入(利用欄位反射,或者set方法反射),其postProcessMergedBeanDefinition
方法先於另外兩個方法執行,它會掃描bean class中的所有方法和欄位,如果具備@Autowired or @Value
註解 那麼會包裝成InjectionMetadata
並快取,後續在determineCandidateConstructors
和postProcessProperties
中發揮作用
在Spring原始碼學習筆記7——Spring bean的初始化 學習過,負責解析@PostConstruct
,@PreDestroy
,@Resource
,在postProcessMergedBeanDefinition
中會把@PostConstruct
,@PreDestroy
標註的方法包裝成LifecycleMetadata
,其中PostConstruct
標註的放法會在postProcessBeforeInitialization(範例化之後,初始化之前)
中被呼叫,PreDestroy
標註的方法在postProcessBeforeDestruction
bean被消耗之前呼叫。@Resource
和@Autowired
類似,都是在postProcessProperties
中呼叫,但是獲取bean的策略有所不同,可以看Spring原始碼學習筆記7——Spring bean的初始化中對二者進行的對比
ApplicationListenerDetector 監聽器探測器,它會在ApplicationListener型別的bean被範例化之後,註冊到上下文的事件多播器中。在ApplicationListener型別的bean被銷燬之前,從上下文持有的事件多播器中刪除
使用AsyncAnnotationAdvisor
對bean進行增強,如果方法上具備@Async
註解,會呼叫AnnotationAsyncExecutionInterceptor
中的invoke
進行增強,實現非同步呼叫,也就是基於Spring AOP的實現。
會掃描每一個bean的方法,如果上面標註了@Scheduled
,@Schedules
註解那麼會被註冊到TaskScheduler
,基於Juc中的定時任務執行緒池原理定時執行。
會從容器中,獲取名稱為applicationEventMulticaster
的bean,如果存在那麼Spring容器將持有此事件多播器,監聽器的註冊,取消註冊,事件的推播都依賴此事件多播器,如果沒有對應的bean那麼使用預設的SimpleApplicationEventMulticaster
。
SimpleApplicationEventMulticaster
:如果設定了Executor
那麼響應事件會呼叫Executor#execute
具體非同步還是同步,取決於Executor
的內部實現(這就是JUC原始碼學習筆記5——執行緒池,FutureTask,Executor框架原始碼解析中提到的Executor的作用——把任務提交與每個任務將如何執行的機制進行解耦),如果沒有那麼就由傳送事件的執行緒進行呼叫(同步)
SpringBoot使用的AnnotationConfigServletWebServerApplicationContext
在這個方法裡面開啟Tomcate伺服器
負責把事件監聽器 註冊到多播器中,並且在事件多播器初始化執行之前,可能存在一些事件沒有處理,這些事件都會放在earlyApplicationEvents集合中,在多播器初始化後,會把earlyApplicationEvents置為null 以後的事件都是直接委託給多播器進行推播。
這個方法貫穿了bean的範例化,屬性填充,初始化。
Spring原始碼學習筆記6——Spring bean的範例化
Spring原始碼學習筆記7——Spring bean的初始化
Spring原始碼學習筆記8——Spring是如何解決迴圈依賴的
這四篇筆記中詳細的說明了Bean的初始化流程,下面我進行粗略的總結,提前初始化單例bean的方法是DefaultListableBeanFactory#preInstantiateSingletons
在DefaultListableBeanFactory#preInstantiateSingletons
存在兩個for迴圈,第一個是範例化載入所有的單例非懶載入bean,第二個for迴圈是在載入完後,呼叫實現了SmartInitializingSingleton
介面bean的afterSingletonsInstantiated
方法,這是spring留給我們的一個擴充套件介面
首先要滿足!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()
,即不是抽象,且單例,且不是懶載入的BeanDefinition
什麼是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——有點懶載入的意思。
上面我們看到初始化所有懶載入單例bean呼叫的是BeanFactory#getBean(beanName)
,這裡的BeanFactory一般是DefaultSingletonBeanRegistry
其內部使用map組成三級快取,並且還要對factoryBan的快取,如下圖
下面我們看下建立bean的邏輯
首先是一級快取即單例池中獲取,如果之前之前建立過單例物件,那麼會被快取到一級快取單例池中,這時候就直接拿到了,但是拿到的可能是FactoryBean
,所有呼叫getObjectForBeanInstance
方法,如果是FactoryBean那麼會呼叫其getObject
生成bean。如果當前BeanFactory沒有此beanName對應的BeanDefinition那麼會呼叫父beanFactroy的getBean
方法。反之說明存在對應的beanDefinition這時候就是取建立bean的流程了
建立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。
建立原型物件和單例物件類似,但是原型物件是不允許迴圈依賴的,beanFactory使用prototypesCurrentlyInCreation
ThreadLocal儲存了原型物件是否處於建立中,如果傳送回圈依賴那麼將丟擲異常
這裡說是範例化,其實是範例化+屬性填充+初始化
之所以說範例化,是因為單例,原型,特殊作用域的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
它是基於ThreadLocal
實現的
基於TransactionSynchronizationManager
實現的,TransactionSynchronizationManager
內部基於ThreadLocal,和SimpleThreadScope
不同的是,Spring建立的邏輯事務,即使在同一執行緒上,如果是一個獨立的事務,spring會先解綁當前執行緒上面的資訊,掛起當前事務,從而導致兩個邏輯事務使用到的bean是不同的bean
什麼樣的,事務被視作時一個獨立事務暱
如果外部不存在一個事務,並且傳播級別是REQUIRED,REQUIRES_NEW,NESTED
如果外部存在一個事務,且傳播級別為REQUIRES_NEW
如果外部存在一個事務,且傳播級別為巢狀事務,但是此時不是通過儲存點來實現巢狀事務
上面三個條件也意味著內部事務和外部事務拿到的bean是不同的
MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
讓我們自定義修改合併後的BeanDefinition(每一個beanDefinition存在一個識別符號記錄是否被處理過後,只會被處理一次)getEarlyBeanReference
方法封裝成ObjectFactory
存入到三級快取,這一步是是為了解決迴圈依賴發生時,需要BeanPostProcessor進行後置處理的問題(Spring原始碼學習筆記8——Spring是如何解決迴圈依賴的,Spring原始碼學習筆記9——構造器注入及其迴圈依賴)@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
,最後resolvableDependencies
map中的候選者,優先於其他候選者。
PropertyValues屬性注入原理
BeanDefinition.getPropertyValues()
這個方法返回bean的屬性需要設定的值,一般在xml設定bean的時候比較常用,但是也被用於類似於Feign
掃描介面構建FactoryBean
的時候指定FactoryBean
的屬性值。我們實際開發中用得比較少.
上圖中的例子就是通過BeanDefinitionRegistryPostProcessor
自定義註冊BeanDefinition
,並且指定了屬性需要注入生麼值,其中str
會被注入字串straaaa
,屬性b是RuntimeBeanReference
會被注入名稱為b
的bean物件。負責這些屬性注入的方法是applyPropertyValues
。最終反射設定值呼叫的是BeanWrapper#setPropertyValues
方法,Spring會先進行型別轉換,然後反射(一般是呼叫對應的set方法)
上面我們學習了Spring是如何範例化bean,並且進行依賴注入的,但是完成這些步驟的bean還沒有執行初始化,比如說標記的初始化方法還沒有被回撥,接下來我們看下是spring是如何實現的
這裡就是判斷是否實現了BeanNameAware
,BeanClassLoaderAware
,BeanFactoryAware
會呼叫對應的set方法
這裡可以看到並沒有呼叫ApplicationContext
因為這是在DefaultListableBeanFactory
中實現的操作,DefaultListableBeanFactory
並不知道ApplicationContext
的存在
就是呼叫所有的BeanPostProcessor#postProcessBeforeInitialization
這裡便會呼叫到
ApplicationContextAwareProcessor
,它會負責回撥EnvironmentAware(環境感知介面)
,EmbeddedValueResolverAware(可以拿到上下文中解析字串值的簡單策略介面實現)
,ResourceLoaderAware(可以拿到上下文中用於載入資源(例如類路徑或檔案系統資源)的策略介面實現)
,ApplicationEventPublisherAware(可以拿到上下文中事件釋出功能的介面實現)
,MessageSourceAware(可以拿到上下文解析訊息的策略介面的實現)
,ApplicationContextAware(spring上下文感知介面)
對應的方法
以及InitDestroyAnnotationBeanPostProcessor
(CommonAnnotationBeanPostProcessor
的子類,其實是呼叫到CommonAnnotationBeanPostProcessor
)的postProcessBeforeInitialization
方法,他會呼叫@PostConstruct
標註的方法。(並且如果容器銷燬bean還會呼叫@PreDestroy
標註的方法)
首先執行InitializingBean#afterPropertiesSet
方法,然後指定自定義在BeanDefinition中的初始化方法。這裡可以看到InitializingBean
和SmartInitializingSingleton
的區別,前者是每一個bean的初始化的時候呼叫,後者是所有單例bean預載入之後呼叫。如果在InitializingBean
中使用容器取getBean那麼可能會觸發其他bean的載入,SmartInitializingSingleton
則不會造成,因為呼叫的時候已經預載入了所有的非懶載入的單例bean
呼叫BeanPostProcessor#postProcessAfterInitialization
這裡便是發生動態代理AOP增強的地方(Spring 原始碼學習筆記10——Spring AOP)
呼叫LifecycleProcessor#onRefresh
方法,推播ContextRefreshedEvent
的事件