【Spring 源碼】—— IoC 之加載 Bean:parentBeanFactory 與依賴處理

若是從單列緩存中沒有獲取到 Bean 對象,則說明有兩種狀況:java

  1. 該 Bean 的 Scope 不是 Singleton
  2. 該 Bean 的 Scope 是 Singleton,可是初始化沒有完成

針對這兩種狀況,Spring 是如何處理的呢?統一加載並完成初始化!這部份內容的篇幅較長,拆分爲兩部分:緩存

  • 第一部分,主要是一些檢測、parentBeanFactory 以及依賴處理。 
  • 第二部分則是各個 scope 的初始化。 

代碼以下:markdown

//AbstractBeanFactory.java

// <3> 由於 Spring 只解決單例模式下得循環依賴,在原型模式下若是存在循環依賴則會拋出異常。
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

// <4> 若是當前容器中沒有找到,則從父類容器中加載
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    String nameToLookup = originalBeanName(name);
    // 若是,父類容器爲 AbstractBeanFactory ,直接遞歸查找
    if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
        // 用明確的 args 從 parentBeanFactory 中,獲取 Bean 對象
    } else if (args != null) {
        return (T)parentBeanFactory.getBean(nameToLookup, args);
        // 用明確的 requiredType 從 parentBeanFactory 中,獲取 Bean 對象
    } else if (requiredType != null) {
        return parentBeanFactory.getBean(nameToLookup, requiredType);
        // 直接使用 nameToLookup 從 parentBeanFactory 獲取 Bean 對象
    } else {
        return (T)parentBeanFactory.getBean(nameToLookup);
    }
}

// <5> 若是不是僅僅作類型檢查則是建立bean,這裏須要記錄
if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
}

try {
    // <6> 從容器中獲取 beanName 相應的 GenericBeanDefinition 對象,並將其轉換爲 RootBeanDefinition 對象
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    // 檢查給定的合併的 BeanDefinition
    checkMergedBeanDefinition(mbd, beanName, args);

    // <7> 處理所依賴的 bean
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
        for (String dep : dependsOn) {
            // 若給定的依賴 bean 已經註冊爲依賴給定的 bean
            // 即循環依賴的狀況,拋出 BeanCreationException 異常
            if (isDependent(beanName, dep)) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
            }
            // 緩存依賴調用 TODO 芋艿
            registerDependentBean(dep, beanName);
            try {
                // 遞歸處理依賴 Bean
                getBean(dep);
            } catch (NoSuchBeanDefinitionException ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
            }
        }
    }
// ... 省略不少代碼
}複製代碼

這段代碼主要處理如下幾個部分:ide

  • <3> 處,原型模式循環依賴檢查,若是當前 Bean 在建立,則拋出異常。詳細解析見「1. 循環依賴檢查」
  • <4> 處,若是 beanDefinitionMap 中不存在 beanName 的 BeanDefinition(即在 Spring bean 初始化過程當中沒有加載),則嘗試從 parentBeanFactory 中加載。詳細解析見「2. 檢查父類 BeanFactory」
  • <5> 處,判斷是否爲類型檢查。詳細解析見「3. 類型檢查」
  • <6> 處,從 mergedBeanDefinitions 中獲取 beanName 對應的 RootBeanDefinition 對象。若是這個 BeanDefinition 是子 Bean 的話,則會合並父類的相關屬性。詳細解析見「4. 獲取 RootBeanDefinition」
  • <7> 處

1. 循環依賴檢查

Spring 只解決單列模式下的循環依賴,對於原型模式的依賴則拋出 BeanCurrentlyInCreationException 異常post

//AbstractBeanFactory.java

// 由於 Spring 只解決單例模式下得循環依賴,在原型模式下若是存在循環依賴則會拋出異常。
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}
複製代碼

調用 AbstractBeanFactory#isPrototypeCurrentlyInCreation(String beanName) 方法,判斷當前 Bean 是否正在建立。代碼以下:ui

//AbstractBeanFactory.java

//ThreaadLocal 緩存建立的Bean
private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation");

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

ThreadLocal 存儲的值在單列模式和原型模式是不同的
this

  • 單列模式存儲的是 Set 集合
  • 原型模式存儲的是 String

2. 檢查父類 BeanFactory

若是當前容器沒有找到 Bean ,就從父容器中加載。代碼以下:
spa

//AbstractBeanFactory.java

// 獲取父容器
BeanFactory parentBeanFactory = getParentBeanFactory();
// 檢查父容器
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // 獲取原始 beanName
    String nameToLookup = originalBeanName(name);
    // 若是,父類容器爲 AbstractBeanFactory ,直接遞歸查找
    if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
        // 用明確的 args 從 parentBeanFactory 中,獲取 Bean 對象
    } else if (args != null) {
        return (T)parentBeanFactory.getBean(nameToLookup, args);
        // 用明確的 requiredType 從 parentBeanFactory 中,獲取 Bean 對象
    } else if (requiredType != null) {
        return parentBeanFactory.getBean(nameToLookup, requiredType);
        // 直接使用 nameToLookup 從 parentBeanFactory 獲取 Bean 對象
    } else {
        return (T)parentBeanFactory.getBean(nameToLookup);
    }
}複製代碼

2.1 獲取父容器

獲取父容器。代碼以下:prototype

//AbstractBeanFactory.java

private BeanFactory parentBeanFactory;

@Override
public BeanFactory getParentBeanFactory() {
    return this.parentBeanFactory;
}複製代碼

2.2 檢查父容器

若父容器不爲空,而且 beanDefinitionMap 中沒有找到對應的 BeanDefinition 對象 。代碼以下:code

//DefaultListableBeanFactory.java

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

@Override
public boolean containsBeanDefinition(String beanName) {
	Assert.notNull(beanName, "Bean name must not be null");
	return this.beanDefinitionMap.containsKey(beanName);
}複製代碼

2.3 獲取原始 beanName

獲取原始 beanName。代碼以下:

//AbstractBeanFactory.java

protected String originalBeanName(String name) {
    String beanName = transformedBeanName(name); // <x>
    if (name.startsWith(FACTORY_BEAN_PREFIX)) { // <y>
        beanName = FACTORY_BEAN_PREFIX + beanName;
    }
    return beanName;
}複製代碼

  • <x> 處,AbstractBeanFactory#transformedBeanName(String name) 方法,是對 name 進行轉換,獲取真正的 beanName。詳情解析前往《【Spring 源碼】—— IoC 之開啓 Bean 的加載》
  • <y> 處,若是 name 是以 "&" 開頭的,則加上 "&"  ,由於在AbstractBeanFactory#transformedBeanName(String name) 方法中把  "&"  去掉,這邊須要補上

2.4 委託父容器加載

這裏都是委託父容器加載 Bean 的邏輯。代碼以下:

// 若是,父類容器爲 AbstractBeanFactory ,直接遞歸查找
if (parentBeanFactory instanceof AbstractBeanFactory) {
    return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
    // 用明確的 args 從 parentBeanFactory 中,獲取 Bean 對象
} else if (args != null) {
    return (T)parentBeanFactory.getBean(nameToLookup, args);
    // 用明確的 requiredType 從 parentBeanFactory 中,獲取 Bean 對象
} else if (requiredType != null) {
    return parentBeanFactory.getBean(nameToLookup, requiredType);
    // 直接使用 nameToLookup 從 parentBeanFactory 獲取 Bean 對象
} else {
    return (T)parentBeanFactory.getBean(nameToLookup);
}複製代碼

3.類型檢查

方法參數 typeCheckOnly,表示僅僅進行類型檢查獲取 Bean 對象。若是不是僅僅作類型檢查,而是建立 Bean 對象,則須要調用 AbstractBeanFactory#markBeanAsCreated(String beanName) 方法,進行記錄。代碼以下:

//AbstractBeanFactory.java

// beanName -> RootBeanDefinition
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
}

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

protected void clearMergedBeanDefinition(String beanName) {
    this.mergedBeanDefinitions.remove(beanName);
}
複製代碼

4. 獲取 RootBeanDefinition

//AbstractBeanFactory.java

// 從容器中獲取 beanName 相應的 GenericBeanDefinition 對象,並將其轉換爲 RootBeanDefinition 對象
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查給定的合併的 BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);複製代碼

調用 AbstractBeanFactory#getMergedLocalBeanDefinition(String beanName) 獲取 RootBeanDefinition 對象。代碼以下:

//AbstractBeanFactory.java
// beanName -> RootBeanDefinition
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // 快速從緩存中獲取,若是不爲空,則直接返回
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    if (mbd != null) {
        return mbd;
    }
    // 若是返回的 BeanDefinition 是子類 bean 的話,則合併父類相關屬性
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
複製代碼
  • 若是 mergedBeanDefinitions 緩存中存在對應的 RootBeanDefinition 對象,則直接返回。
  • 不然調用AbstractBeanFactory#getMergedBeanDefinition(String beanName, BeanDefinition bd)獲取 RootBeanDefinition 對象
    • 經過 AbstractBeanFactory#getBeanDefinition(String beanName) 獲取 BeanDefinition 對象,若是沒有找到就拋出 NoSuchBeanDefinitionException 異常。代碼以下:
// DefaultListableBeanFactory.java

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

// AbstractBeanFactory.java
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
    BeanDefinition bd = this.beanDefinitionMap.get(beanName);
    if (bd == null) {
        if (logger.isTraceEnabled()) {
            logger.trace("No bean named '" + beanName + "' found in " + this);
        }
        throw new NoSuchBeanDefinitionException(beanName);
    }
    return bd;
}複製代碼

5.

相關文章
相關標籤/搜索