讀完這篇文章你將會收穫到web
Spring
中
prototype
類型的
bean
如何作循環依賴檢測
Spring
中
singleton
類型的
bean
如何作循環依賴檢測
繼上一篇文章 Spring 獲取單例流程(一) 咱們此次繼續往下分析一下後面的流程spring
上一篇文章中咱們說到,首先咱們根據 name
找到其對應的 beanName
、而後去緩存中看是否已經建立了/建立中這個對應的 bean
,若是在緩存中找到了這個 bean
、那麼咱們須要對這個 bean
可能進行一些處理,好比說用戶想要的是一個普通的 bean
、可是在 Spring
緩存中找到的是一個 factoryBean
、這個時候就要調用 fatoryBean
的 getObject
方法以及對應的一些前置方法以及回調等。緩存
那麼若是咱們在緩存中找不到這個 bean
那麼流程又是怎麼樣?這個就是這篇文章要跟你們一塊兒分享的編輯器
if (sharedInstance != null && args == null) {
// 這裏被我刪除了一些spring 的log // 處理一下 factory bean 的狀況、包括從 factory beans 的緩存中獲取、或者從新調用 factory bean 的 get bean 方法 包括一些回調 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 從 上面的 getSingleton 拿不到對象的bean 、說明這個bean的scope 要麼不是 singleton 要這個bean是singleton 可是沒有初始化一句 // 由於 Spring 只解決單例模式下得循環依賴,在原型模式下若是存在循環依賴則會拋出異常 // 這裏的循環依賴檢查使用的 是 threadLocal 由於 prototype 類型的只是 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 若是容器中沒有找到,則從父類容器中加載 BeanFactory parentBeanFactory = getParentBeanFactory(); // parentBeanFactory 不爲空且 beanDefinitionMap 中不存該 name 的 BeanDefinition if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. // 這裏只是找出他的真正的beanName、並無去掉 factory bean 的前綴 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } ....... ....... ........ } 複製代碼
第一步就是判斷這個是不是一個 prototype
類型的 bean
,若是是而且正在建立中、那麼就拋出一個循環依賴的異常源碼分析
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
// prototypesCurrentlyInCreation 是一個 threadLocal Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); } 複製代碼
每個 prototype
的 bean
建立的時候都會調用下面這個方法flex
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal == null) { this.prototypesCurrentlyInCreation.set(beanName); } else if (curVal instanceof String) { Set<String> beanNameSet = new HashSet<>(2); beanNameSet.add((String) curVal); beanNameSet.add(beanName); this.prototypesCurrentlyInCreation.set(beanNameSet); } else { Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.add(beanName); } } 複製代碼
curVal
要麼是一個 String
要麼是一個 Set
, 而在建立 prototype bean
完成以後ui
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal instanceof String) { this.prototypesCurrentlyInCreation.remove(); } else if (curVal instanceof Set) { Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.remove(beanName); if (beanNameSet.isEmpty()) { this.prototypesCurrentlyInCreation.remove(); } } } 複製代碼
能夠看到 Spring
使用 ThreadLocal
去作一個循環依賴的檢測、咱們在 Spring
資源加載的源碼分析裏面也說起到了、也是使用 ThreadLocal
進行一個資源的循環引用的檢測 Spring 容器的初始化this
第二步則是判斷當前的 beanFactory
是否有父容器(父 beanFactory
) ,若是有而且 beanName
的 beanDefinition
不存在當前的 beanFactory
中,那麼則嘗試在父容器中去獲取這個 bean
url
咱們繼續往下看下面的代碼spa
// 若是不是僅僅作類型檢查則是建立bean,標記這個bean 已經建立了或者將要被建立
if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 從容器中獲取 beanName 相應的 GenericBeanDefinition,並將其轉換爲 RootBeanDefinition final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 檢查給定的合併的 BeanDefinition checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // 處理所依賴的 bean String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // 若是是循環依賴 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // 註冊 registerDependentBean(dep, beanName); try { // 看看我依賴的大佬好了沒 getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } ...... ...... 複製代碼
第三步則是從 BeanDefinitionRegistry
中獲取註冊的 BeanDefinition
繼而獲取這個 bean
所要依賴的其餘 bean
,遍歷其所依賴的 bean
、判斷是否循環依賴了
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) { return isDependent(beanName, dependentBeanName, null); } } private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) { if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } String canonicalName = canonicalName(beanName); // 找出依賴這個beanName的集合 Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); // 沒有人依賴這個beanName if (dependentBeans == null) { return false; } // 哦嚯、beanName 依賴的 bean、也依賴着beanName、完蛋 if (dependentBeans.contains(dependentBeanName)) { return true; } // 看看依賴 beanName 的 其餘依賴、有沒有被dependentBeanName 依賴 // A 想依賴F、BCDE 依賴着A、那麼咱們如今來到這一步、已經肯定了F不依賴A、那麼咱們要看看F是否依賴BCDE、若是依賴、那麼就是循環依賴了 for (String transitiveDependency : dependentBeans) { if (alreadySeen == null) { alreadySeen = new HashSet<>(); } alreadySeen.add(beanName); if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } return false; } 複製代碼
每個 bean
建立以前都會註冊其依賴關係、主要由兩個 Map
組成、一個是 key
爲被依賴者,value
爲依賴者集合,另外一個則是 key
爲依賴者,value
爲被依賴者集合,好比說 beanA
依賴着 beanB
和 beanC
key 爲被依賴者 value 爲依賴者集合 beanB ---> beanA beanC ---> beanA key 爲依賴者,value 爲被依賴者集合 beanA ---> beanB,beanC 複製代碼
第四步則是去註冊依賴關係,也就是往上面的兩個 Map
中存放數據
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName); // 在這個裏面加上 這個依賴個人人 synchronized (this.dependentBeanMap) { Set<String> dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8)); if (!dependentBeans.add(dependentBeanName)) { return; } } // 在這裏將我依賴的 那個大佬放進去我依賴的列表中 synchronized (this.dependenciesForBeanMap) { Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); dependenciesForBean.add(canonicalName); } } 複製代碼
最後的 getBean
則回到咱們最初的起點
getBean(dep);
複製代碼
今天咱們就先分析到這裏、後續的話咱們在後面的文章繼續探討。今天咱們大體分析了
name
找出對應的
beanName
、不管這個
name
是別名或者是一個
factoryBean
的
beanName
beanName
對象
singletonObjects
中看看有沒有
earlySingletonObjects
singletonFactories
中看看有沒有
bean
、那麼咱們仍是須要處理一下這個
bean
Spring
緩存中返回的
bean
是
factoryBean
、而用戶也想要的是一個
beanFactory
(參數
name
中的前綴是
&
)、那麼咱們直接返回
Spring
緩存中返回的
bean
是普通的
bean
、而用戶也想要的是一個普通的
bean
、那麼就直接返回
Spring
緩存中返回的
bean
是一個
factoryBean
、而用戶想要的是一個普通的
bean
、那麼咱們就要從
factoryBean
中獲取這個
bean
factoryBean
中獲取這個
bean
的過程當中、須要調用到前置處理、後置處理和咱們經常使用的接口回調
BeanPostProcessor
bean
、則判斷是不是
prototype
類型而且循環依賴
bean
beanName
對應的
beanDefinition
找出其依賴的
beanName
beanName
與 依賴的
beanName
是否循環依賴、沒有則註冊其依賴關係並調用
getBean
方法去建立依賴的
beanName