【Spring 源碼】—— IoC 之開啓 Bean 的加載

1.getBean

當咱們經過使用 BeanFactory#getBean(String name) 方法來獲取 Bean 時候,就會觸發 加載  Bean 階段。代碼以下

//AbstractBeanFactory.java

@Override
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}複製代碼

實際調用的是 AbstractBeanFactory#doGetBean(String name, Class<T> requiredType,Object[] args, boolean typeCheckOnly) 方法,該方法接受四個參數:java

  • name :要獲取 Bean 名字
  • requiredType :要獲取 Bean 類型
  • args :建立 Bean 時傳遞的參數,該參數只會在建立 Bean 時有效
  • typeCheckOnly :是否爲類型檢測

2.doGetBean

AbstractBeanFactory#doGetBean(String name, Class<T> requiredType,Object[] args, boolean typeCheckOnly)  方法就是加載 Bean 核心邏輯,代碼較長,須要慢慢看。代碼以下:

//AbstractBeanFactory.java

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    //<1> 返回 bean 名稱,剝離工廠引用前綴。
    // 若是 name 是 alias ,則獲取對應映射的 beanName 。
    final String beanName = transformedBeanName(name);
    Object bean;

    // 從緩存中或者實例工廠中獲取 Bean 對象
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                    "' that is not fully initialized yet - a consequence of a circular reference");
            } else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        // <2> 完成 FactoryBean 的相關處理,並用來獲取 FactoryBean 的處理結果
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        // <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);
                    }
                }
            }

            // <8> bean 實例化
            if (mbd.isSingleton()) { // 單例模式
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    } catch (BeansException ex) {
                        // 顯式從單例緩存中刪除 Bean 實例
                        // 由於單例模式下爲了解決循環依賴,可能他已經存在了,因此銷燬它。 TODO 芋艿
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } else if (mbd.isPrototype()) { // 原型模式
                Object prototypeInstance;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                } finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            } else {
                // 得到 scopeName 對應的 Scope 對象
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    // 從指定的 scope 下建立 bean
                    Object scopedInstance = scope.get(beanName, () -> {
                        // 加載前置處理
                        beforePrototypeCreation(beanName);
                        try {
                            // 建立 Bean 對象
                            return createBean(beanName, mbd, args);
                        } finally {
                            // 加載後綴處理
                            afterPrototypeCreation(beanName);
                        }
                    });
                    // 從 Bean 實例中獲取對象
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                } catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                        "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                        ex);
                }
            }
        } catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // <9> 檢查須要的類型是否符合 bean 的實際類型
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            // 執行轉換
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            // 轉換失敗,拋出 BeanNotOfRequiredTypeException 異常
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        } catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                    ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T)bean;
}複製代碼

這段代碼很是長,並且邏輯超複雜,咱們將拆解這段代碼進行分析緩存

2.1 獲取 beanName

對應代碼段以下:markdown

//AbstractBeanFactory.java

final String beanName = transformedBeanName(name);複製代碼

這裏的 name 不必定是 beanName,多是 aliasName,也有多是 FactoryBean,因此這裏須要調用 AbstractBeanFactory#transformedBeanName(String name)  方法,對 name 進行轉換。代碼段以下:ide

//AbstractBeanFactory.java

protected String transformedBeanName(String name) {
	return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}複製代碼
  • 調用 BeanFactoryUtils#transformedBeanName(String name) 方法,剝離 FactoryBean 修飾符。代碼段以下:

//BeanFactoryUtils.java

//緩存已經轉換好的 name to beanName
private static final Map<String, String> transformedBeanNameCache = new ConcurrentHashMap<>();

public static String transformedBeanName(String name) {
	Assert.notNull(name, "'name' must not be null");
	if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
		return name;
	}
        // name = "&userService" 
        // beanName = "userService"
	return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
		do {
		    beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
		}
		while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
		return beanName;
	});
}複製代碼
  • 調用 BeanFactoryUtils#canonicalName(String name) 方法,獲取 alias 最終映射的 beanName,這是一個循環獲取的過程

//SimpleAliasRegistry.java
//緩存 alias to beanName
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

public String canonicalName(String name) {
	String canonicalName = name;
	String resolvedName;
	// 循環,從 aliasMap 中,獲取到最終映射的 beanName
	do {
	    resolvedName = this.aliasMap.get(canonicalName);
	    if (resolvedName != null) {
	        canonicalName = resolvedName;
	    }
	} while (resolvedName != null);
	return canonicalName;
}
複製代碼

2.2 從單例 Bean 緩存中獲取 Bean

對應代碼段以下:post

//AbstractBeanFactory.java

//從緩存或實例工廠中獲取 Bean 對象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
    if (logger.isTraceEnabled()) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
        } else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
        }
    }
    // <x> 完成 FactoryBean 的相關處理,並用來獲取 FactoryBean 的處理結果
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}複製代碼

  • 單列 Bean 再整個生命週期只會建立一次,而且把已經建立好的 Bean 存入緩存,這樣下一次獲取 Bean 時候直接從單列緩存中獲取
  • <x> 處,若是從緩存裏面獲得了 Bean 對象,還需調用 AbstractBeanFactory#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) 來實例化 Bean,由於緩存中的 Bean 有多是最原始的 Bean ,不必定是實例化了的 Bean

詳細解析,見 《【Spring 源碼】—— IoC 之加載 Bean:從單例緩存中獲取單》ui

2.3 原型模式依賴檢查

對應代碼段以下:this

//AbstractBeanFactory.java

//Spring 只處理單列模式下的循環依賴
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}複製代碼

Spring 只處理單例模式下得循環依賴,對於原型模式的循環依賴直接拋出異常。主要緣由仍是在於,和 Spring 解決循環依賴的策略有關。
spa

  • 單列( Singleton )模式:Spring 再建立 Bean 的時候並非等 Bean 徹底建立完成後纔會把 Bean 添加到緩存中,而是不等 Bean 建立完成就會將建立的 Bean 的 ObjectFactory 提前放在緩存中,這樣一旦下一個 Bean 建立的時候須要依賴 Bean 時直接使用 ObjectFactory
  • 原型( Prototype )模式:無法使用緩存,循環依賴沒法處理

詳細解析,見 《【Spring 源碼】—— IoC 之加載 Bean:parentBeanFactory 與依賴處理》
prototype

2.4 從 parentBeanFactory 獲取 Bean

對應代碼段以下:
code

// AbstractBeanFactory.java

// 若是當前容器中沒有找到,則從父類容器中加載
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);
    }
}複製代碼

若是當前容器中沒有相對應的 BeanDefinition 對象,則會嘗試從父容器中加載,而後遞歸調用 getBean(...) 方法。

詳細解析,見 《【Spring 源碼】—— IoC 之加載 Bean:parentBeanFactory 與依賴處理》

2.5 指定的 Bean 標記爲已經建立或即將建立

對應代碼段以下:

// AbstractBeanFactory.java

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

詳細解析,見 《【Spring 源碼】—— IoC 之加載 Bean:parentBeanFactory 與依賴處理》

2.6 獲取 BeanDefinition

對應代碼段以下:

// AbstractBeanFactory.java

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

..todo

相關文章
相關標籤/搜索