歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之parentBeanFactory 與依賴處理

精品專欄

 

繼上篇部落格 【死磕 Spring】—– 載入 bean 之 快取中獲取單例 bean,如果從單例快取中沒有獲取到單例 bean,則說明兩種情況:

  1. 該 bean 的 scope 不是 singleton

  2. 該 bean 的 scope 是 singleton ,但是沒有初始化完成

針對這兩種情況 Spring 是如何處理的呢?統一載入並完成初始化!這部分內容的篇幅較長,拆分為兩部分,第一部分主要是一些檢測、parentBeanFactory 以及依賴處理,第二部分則是各個 scope 的初始化。

  1.            if (isPrototypeCurrentlyInCreation(beanName)) {

  2.                throw new BeanCurrentlyInCreationException(beanName);

  3.            }

  4.            // Check if bean definition exists in this factory.

  5.            BeanFactory parentBeanFactory = getParentBeanFactory();

  6.            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {

  7.                // Not found -> check parent.

  8.                String nameToLookup = originalBeanName(name);

  9.                if (parentBeanFactory instanceof AbstractBeanFactory) {

  10.                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(

  11.                            nameToLookup, requiredType, args, typeCheckOnly);

  12.                }

  13.                else if (args != null) {

  14.                    // Delegation to parent with explicit args.

  15.                    return (T) parentBeanFactory.getBean(nameToLookup, args);

  16.                }

  17.                else {

  18.                    // No args -> delegate to standard getBean method.

  19.                    return parentBeanFactory.getBean(nameToLookup, requiredType);

  20.                }

  21.            }

  22.            if (!typeCheckOnly) {

  23.                markBeanAsCreated(beanName);

  24.            }

  25.            try {

  26.                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

  27.                checkMergedBeanDefinition(mbd, beanName, args);

  28.                // Guarantee initialization of beans that the current bean depends on.

  29.                String[] dependsOn = mbd.getDependsOn();

  30.                if (dependsOn != null) {

  31.                    for (String dep : dependsOn) {

  32.                        if (isDependent(beanName, dep)) {

  33.                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  34.                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");

  35.                        }

  36.                        registerDependentBean(dep, beanName);

  37.                        try {

  38.                            getBean(dep);

  39.                        }

  40.                        catch (NoSuchBeanDefinitionException ex) {

  41.                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  42.                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);

  43.                        }

  44.                    }

  45.                }

  46.            }

  47.            // 省略很多程式碼

這段程式碼主要處理如下幾個部分:

  1. 檢測。若當前 bean 在建立,則丟擲 BeanCurrentlyInCreationException 異常。

  2. 如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition(即在 Spring bean 初始化過程中沒有載入),則嘗試從 parentBeanFactory 中載入。

  3. 判斷是否為型別檢查。

  4. 從 mergedBeanDefinitions 中獲取 beanName 對應的 RootBeanDefinition,如果這個 BeanDefinition 是子 Bean 的話,則會合併父類的相關屬性。

  5. 依賴處理。

檢測

在前面就提過,Spring 只解決單例樣式下的迴圈依賴,對於原型樣式的迴圈依賴則是丟擲 BeanCurrentlyInCreationException 異常,所以首先檢查該 beanName 是否處於原型樣式下的迴圈依賴。如下:

  1. if (isPrototypeCurrentlyInCreation(beanName)) {

  2.    throw new BeanCurrentlyInCreationException(beanName);

  3. }

呼叫 isPrototypeCurrentlyInCreation() 判斷當前 bean 是否正在建立,如下:

  1.    protected boolean isPrototypeCurrentlyInCreation(String beanName) {

  2.        Object curVal = this.prototypesCurrentlyInCreation.get();

  3.        return (curVal != null &&

  4.                (curVal.equals(beanName) || (curVal instanceof Set && ((Set>) curVal).contains(beanName))));

  5.    }

其實檢測邏輯和單例樣式一樣,一個“集合”存放著正在建立的 bean,從該集合中進行判斷即可,只不過單例樣式的“集合”為 Set ,而原型樣式的則是 ThreadLocal,prototypesCurrentlyInCreation 定義如下:

  1.    private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation");

檢查父類 BeanFactory

若 containsBeanDefinition 中不存在 beanName 相對應的 BeanDefinition,則從 parentBeanFactory 中獲取。

  1.    // 獲取 parentBeanFactory

  2.    BeanFactory parentBeanFactory = getParentBeanFactory();

  3.    // parentBeanFactory 不為空且 beanDefinitionMap 中不存該 name 的 BeanDefinition

  4.    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {

  5.        // 確定原始 beanName

  6.        String nameToLookup = originalBeanName(name);

  7.        // 若為 AbstractBeanFactory 型別,委託父類處理

  8.        if (parentBeanFactory instanceof AbstractBeanFactory) {

  9.            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(

  10.                    nameToLookup, requiredType, args, typeCheckOnly);

  11.        }

  12.        else if (args != null) {

  13.            // 委託給建構式 getBean() 處理

  14.            return (T) parentBeanFactory.getBean(nameToLookup, args);

  15.        }

  16.        else {

  17.            // 沒有 args,委託給標準的 getBean() 處理

  18.            return parentBeanFactory.getBean(nameToLookup, requiredType);

  19.        }

  20.    }

整個過程較為簡單,都是委託 parentBeanFactory 的 getBean() 進行處理,只不過在獲取之前對 name 進行簡單的處理,主要是想獲取原始的 beanName,如下:

  1.    protected String originalBeanName(String name) {

  2.        String beanName = transformedBeanName(name);

  3.        if (name.startsWith(FACTORY_BEAN_PREFIX)) {

  4.            beanName = FACTORY_BEAN_PREFIX + beanName;

  5.        }

  6.        return beanName;

  7.    }

transformedBeanName() 是對 name 進行轉換,獲取真正的 beanName,因為我們傳遞的可能是 aliasName(這個過程在部落格 【死磕 Spring】----- 載入 bean 之 開啟 bean 的載入 中分析 transformedBeanName() 有詳細說明),如果 name 是以 “&” 開頭的,則加上 “&”,因為在 transformedBeanName() 將 “&” 去掉了,這裡補上。

型別檢查

引數 typeCheckOnly 是用來判斷呼叫 getBean() 是否為型別檢查獲取 bean。如果不是僅僅做型別檢查則是建立bean,則需要呼叫 markBeanAsCreated() 記錄:

  1.     protected void markBeanAsCreated(String beanName) {

  2.        // 沒有建立

  3.        if (!this.alreadyCreated.contains(beanName)) {

  4.            // 加上全域性鎖

  5.            synchronized (this.mergedBeanDefinitions) {

  6.                // 再次檢查一次:DCL 雙檢查樣式

  7.                if (!this.alreadyCreated.contains(beanName)) {

  8.                    // 從 mergedBeanDefinitions 中刪除 beanName,

  9.                    // 併在下次訪問時重新建立它。

  10.                    clearMergedBeanDefinition(beanName);

  11.                    // 新增到已建立bean 集合中

  12.                    this.alreadyCreated.add(beanName);

  13.                }

  14.            }

  15.        }

  16.    }

獲取 RootBeanDefinition

  1. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

呼叫 getMergedLocalBeanDefinition() 獲取相對應的 BeanDefinition,如下:

  1.    protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {

  2.        // 快速從快取中獲取,如果不為空,則直接傳回

  3.        RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);

  4.        if (mbd != null) {

  5.            return mbd;

  6.        }

  7.        // 獲取 RootBeanDefinition,

  8.        // 如果傳回的 BeanDefinition 是子類 bean 的話,則合併父類相關屬性

  9.        return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));

  10.    }

首先直接從 mergedBeanDefinitions 快取中獲取相應的 RootBeanDefinition,如果存在則直接傳回否則呼叫 getMergedBeanDefinition() 獲取 RootBeanDefinition,若獲取的 BeanDefinition 為子 BeanDefinition,則需要合併父類的相關屬性。

處理依賴

如果一個 bean 有依賴 bean 的話,那麼在初始化該 bean 時是需要先初始化它所依賴的 bean。

  1.        // 獲取依賴。

  2.        // 在初始化 bean 時解析 depends-on 標簽時設定

  3.        String[] dependsOn = mbd.getDependsOn();

  4.        if (dependsOn != null) {

  5.            // 迭代依賴

  6.        for (String dep : dependsOn) {

  7.            // 檢驗依賴的bean 是否已經註冊給當前 bean 獲取其他傳遞依賴bean

  8.            if (isDependent(beanName, dep)) {

  9.                throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  10.                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");

  11.            }

  12.            // 註冊到依賴bean中

  13.            registerDependentBean(dep, beanName);

  14.            try {

  15.                // 呼叫 getBean 初始化依賴bean

  16.                getBean(dep);

  17.            }

  18.            catch (NoSuchBeanDefinitionException ex) {

  19.                throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  20.                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);

  21.            }

  22.        }

  23.    }

這段程式碼邏輯是:透過迭代的方式依次對依賴 bean 進行檢測、校驗,如果透過則呼叫 getBean() 實體化依賴 bean。

isDependent() 是校驗該依賴是否已經註冊給當前 bean。

  1.    protected boolean isDependent(String beanName, String dependentBeanName) {

  2.        synchronized (this.dependentBeanMap) {

  3.            return isDependent(beanName, dependentBeanName, null);

  4.        }

  5.    }

同步加鎖給 dependentBeanMap 物件,然後呼叫 isDependent() 校驗。dependentBeanMap 物件儲存的是依賴 beanName 之間的對映關係:beanName - > 依賴 beanName 的集合

  1.    private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {

  2.        // alreadySeen 已經檢測的依賴 bean

  3.        if (alreadySeen != null && alreadySeen.contains(beanName)) {

  4.            return false;

  5.        }

  6.        // 獲取原始 beanName

  7.        String canonicalName = canonicalName(beanName);

  8.        // 獲取當前 beanName 的依賴集合

  9.        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);

  10.        // 不存在依賴,傳回false

  11.        if (dependentBeans == null) {

  12.            return false;

  13.        }

  14.        // 存在,則證明存在已經註冊的依賴

  15.        if (dependentBeans.contains(dependentBeanName)) {

  16.            return true;

  17.        }

  18.        // 遞迴檢測依賴

  19.        for (String transitiveDependency : dependentBeans) {

  20.            if (alreadySeen == null) {

  21.                alreadySeen = new HashSet<>();

  22.            }

  23.            alreadySeen.add(beanName);

  24.            if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {

  25.                return true;

  26.            }

  27.        }

  28.        return false;

  29.    }

如果校驗成功,則呼叫 registerDependentBean() 將該依賴進行註冊,便於在銷毀 bean 之前對其進行銷毀。

  1.    public void registerDependentBean(String beanName, String dependentBeanName) {

  2.        String canonicalName = canonicalName(beanName);

  3.        synchronized (this.dependentBeanMap) {

  4.            Set<String> dependentBeans =

  5.                    this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));

  6.            if (!dependentBeans.add(dependentBeanName)) {

  7.                return;

  8.            }

  9.        }

  10.        synchronized (this.dependenciesForBeanMap) {

  11.            Set<String> dependenciesForBean =

  12.                    this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));

  13.            dependenciesForBean.add(canonicalName);

  14.        }

  15.    }

其實將就是該對映關係儲存到兩個集合中:dependentBeanMap、dependenciesForBeanMap。

最後呼叫 getBean() 實體化依賴 bean。

至此,載入 bean 的第二個部分也分析完畢了,下篇開始分析第三個部分:各大作用域 bean 的處理。

【死磕 Spring】----- IOC 之 IOC 初始化總結

【死磕 Spring】----- IOC 之解析 bean 標簽:解析自定義標簽

【死磕 Spring】----- IOC 之解析 bean 標簽:constructor-arg、property 子元素

【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method)

【死磕 Spring】----- IOC 之解析 bean 標簽:開啟解析行程

【死磕 Spring】----- IOC 之 獲取 Document 物件

【死磕 Spring】----- IOC 之 註冊 BeanDefinition

【死磕 Spring】----- IOC 之 獲取驗證模型

【死磕 Spring】----- IOC 之 Spring 統一資源載入策略

【死磕 Spring】----- IOC 之深入理解 Spring IoC

END


我是 Java 技術驛站,感謝有你


>>>>>> 加群交流技術 <<<<<<

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖