1. 程式人生 > >spring深入學習(十六) IOC 之parentBeanFactory 與依賴處理

spring深入學習(十六) IOC 之parentBeanFactory 與依賴處理

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

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

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

            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                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 {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                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);
                        }
                    }
                }
            }
            // 省略很多程式碼

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

  1. 檢測。若當前 bean 在建立,則丟擲 BeanCurrentlyInCreationException 異常。
  2. 如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition(即在 Spring bean 初始化過程中沒有載入),則嘗試從 parentBeanFactory 中載入。
  3. 判斷是否為型別檢查。
  4. 從 mergedBeanDefinitions 中獲取 beanName 對應的 RootBeanDefinition,如果這個 BeanDefinition 是子 Bean 的話,則會合並父類的相關屬性。
  5. 依賴處理。

檢測

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

if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

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

    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        return (curVal != null &&
                (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }

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

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

檢查父類 BeanFactory

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

    // 獲取 parentBeanFactory
    BeanFactory parentBeanFactory = getParentBeanFactory();
    // parentBeanFactory 不為空且 beanDefinitionMap 中不存該 name 的 BeanDefinition
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        // 確定原始 beanName
        String nameToLookup = originalBeanName(name);
        // 若為 AbstractBeanFactory 型別,委託父類處理
        if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                    nameToLookup, requiredType, args, typeCheckOnly);
        }
        else if (args != null) {
            // 委託給建構函式 getBean() 處理
            return (T) parentBeanFactory.getBean(nameToLookup, args);
        }
        else {
            // 沒有 args,委託給標準的 getBean() 處理
            return parentBeanFactory.getBean(nameToLookup, requiredType);
        }
    }

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

    protected String originalBeanName(String name) {
        String beanName = transformedBeanName(name);
        if (name.startsWith(FACTORY_BEAN_PREFIX)) {
            beanName = FACTORY_BEAN_PREFIX + beanName;
        }
        return beanName;
    }

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

型別檢查

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

     protected void markBeanAsCreated(String beanName) {
        // 沒有建立
        if (!this.alreadyCreated.contains(beanName)) {
            // 加上全域性鎖
            synchronized (this.mergedBeanDefinitions) {
                // 再次檢查一次:DCL 雙檢查模式
                if (!this.alreadyCreated.contains(beanName)) {
                    // 從 mergedBeanDefinitions 中刪除 beanName,
                    // 並在下次訪問時重新建立它。
                    clearMergedBeanDefinition(beanName);
                    // 新增到已建立bean 集合中
                    this.alreadyCreated.add(beanName);
                }
            }
        }
    }

獲取 RootBeanDefinition

final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

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

    protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
        // 快速從快取中獲取,如果不為空,則直接返回
        RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        if (mbd != null) {
            return mbd;
        }
        // 獲取 RootBeanDefinition,
        // 如果返回的 BeanDefinition 是子類 bean 的話,則合併父類相關屬性
        return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
    }

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

處理依賴

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

        // 獲取依賴。
        // 在初始化 bean 時解析 depends-on 標籤時設定
        String[] dependsOn = mbd.getDependsOn();
        if (dependsOn != null) {
            // 迭代依賴
        for (String dep : dependsOn) {
            // 檢驗依賴的bean 是否已經註冊給當前 bean 獲取其他傳遞依賴bean
            if (isDependent(beanName, dep)) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
            }
            // 註冊到依賴bean中
            registerDependentBean(dep, beanName);
            try {
                // 呼叫 getBean 初始化依賴bean
                getBean(dep);
            }
            catch (NoSuchBeanDefinitionException ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
            }
        }
    }

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

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

    protected boolean isDependent(String beanName, String dependentBeanName) {
        synchronized (this.dependentBeanMap) {
            return isDependent(beanName, dependentBeanName, null);
        }
    }

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

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

        // alreadySeen 已經檢測的依賴 bean
        if (alreadySeen != null && alreadySeen.contains(beanName)) {
            return false;
        }
        // 獲取原始 beanName
        String canonicalName = canonicalName(beanName);
        // 獲取當前 beanName 的依賴集合
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        // 不存在依賴,返回false
        if (dependentBeans == null) {
            return false;
        }
        // 存在,則證明存在已經註冊的依賴
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        // 遞迴檢測依賴
        for (String transitiveDependency : dependentBeans) {
            if (alreadySeen == null) {
                alreadySeen = new HashSet<>();
            }
            alreadySeen.add(beanName);
            if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
                return true;
            }
        }
        return false;
    }

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

    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);
        }
    }

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

最後呼叫 getBean() 例項化依賴 bean。

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