【Spring源碼學(xué)習(xí)】getBean (上)

學(xué)習(xí)和了解下spring中g(shù)enBean函數(shù)具體的細節(jié),其中包含了@value是如何和配置文件中的鍵值對關(guān)聯(lián)上以及實例類的引用巾乳,這兩部分打算分為上下兩篇文章去詳細介紹下主穗。本文先介紹注解的值是如何和bean綁定并且填充的茎刚。

接下來使用如下的小demo具體學(xué)習(xí)和了解下。

public class Student {

    @Value("${name}")
    private String className;
    // 使用了@value注解谎僻,其中名稱是name
    // 我們本章就是具體學(xué)習(xí)這個值是怎么樣的方式娄柳,在什么時候注入進去的

    @Override
    public String toString() {
        return "Student{" +
                "className='" + className + '\'' +
                '}';
    }
}
public class Bootstrap {

    public static void main(String[] args){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("context.xml");

        Student student = (Student)applicationContext.getBean("student");
        // getBean 入口
        System.out.println(student.toString());
    }
}
name = CS-2
// 配置了name這個鍵值對,最后被填入Student類中

在具體分析源碼之前想象下如果我們是這個項目的開發(fā)者艘绍,我們需要考慮到哪些問題赤拒?

  • 現(xiàn)在已知的存在不同類型(scope)的bean被存儲在容器中
  • 把獲取的bean列表 循環(huán)一遍依次實例化、填充數(shù)據(jù)
  • 如果存在引用的則需要在實例化之前先行實例化引用類
  • 各種不同類型的容器诱鞠,例如實例化好的挎挖,正在實例化的、正在被銷毀的航夺、已經(jīng)消耗的
  • 填充數(shù)據(jù)這部分操作得支持@value等類似操作
  • 對外提供各種接口蕉朵,便于用戶可以自定義操作bean(例如BeanPostProcess)
  • 相互引用的情況,A引用B阳掐,B引用A該如何解決
  • 各種緩存容器始衅,提高效率
  • 還有可能存在的并發(fā)問題

接下來就看看具體的源碼細節(jié),在哪些地方會解決我們的疑惑

AbstractBeanFactory 類的doGetBean函數(shù)

protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {

    final String beanName = transformedBeanName(name);
    // 根據(jù)傳入的bean的名稱獲取實際的beanName
    // 在這里其實就是看起是否為工廠bean還是bean工廠
    // 后續(xù)會有一篇重點分析下工廠bean和bean工廠的差異
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    // 從已經(jīng)創(chuàng)建好的singletonObjects集合中驗證是否已經(jīng)存在該實例缭保,已經(jīng)存在了就沒必要再調(diào)用生成了汛闸,不過需要注意到單例才是這樣的,針對非單例的bean不是這樣的
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        // 已經(jīng)存在了這樣的實例數(shù)據(jù)了
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // 如果是當(dāng)前處理的beanName已經(jīng)在prototype容器中(同一個線程)被處理艺骂,則拋出異常
        // 看源碼會發(fā)現(xiàn)是存儲在ThreadLocal中的
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // 如果父類存在而且當(dāng)前需要處理的beanName還不在容器中
            // 需要由父類去調(diào)用生成相關(guān)的實例對象
            String nameToLookup = originalBeanName(name);
            // 這個originalBeanName就是去掉name中存在的多于的&诸老,至多只保留一個&
            if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                // 沒有參數(shù)
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        if (!typeCheckOnly) {
          // 傳入的默認typeCheckOnly是false,把當(dāng)前的beanName加入到準(zhǔn)備實例化的alreadyCreated的容器中彻亲,存在并發(fā)的情況孕锄,源碼使用了synchronized
            markBeanAsCreated(beanName);
        }
        
        // 上述步驟完成,對一個普通的bean而言就是加入到準(zhǔn)備實例化的容器中

        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 綜合beanName本身映射的beandefinition和可能存在的父類beandefinition的屬性
            // 存儲到一個全新的對象RootBeanDefinition中
            checkMergedBeanDefinition(mbd, beanName, args);

            // 當(dāng)前bean引用的其他bean苞尝,那很明顯需要先實例化依賴的bean畸肆,再實例化本身
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    if (isDependent(beanName, dep)) {
                       // 如果當(dāng)前beanName和dep存在相互依賴的情況,拋出異常
                       // 可是有個問題宙址,不知道大家想到?jīng)]有轴脐?
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    registerDependentBean(dep, beanName);
                    // 往當(dāng)前的dependentBeanMap和dependenciesForBeanMap填入依賴關(guān)系
                    // 其中dependentBeanMap 是存儲的當(dāng)前對象依賴什么
                    // dependenciesForBeanMap 是被誰依賴被當(dāng)前對象所依賴
                    getBean(dep);
                    // 每一個依賴的bean實例化
                }
            }
            
            // 以上完成了bean的依賴問題,那就開始實例化本身了

            if (mbd.isSingleton()) {
                 // 是單例類型
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                          // 這個才是真正的創(chuàng)建bean對象的步驟B丈啊4笤邸!
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
....

在上述代碼中有提一個疑問注益,關(guān)于循環(huán)依賴的碴巾。循環(huán)依賴是A引用B的同時B也引用A,看上去形成了循環(huán)依賴丑搔,又如現(xiàn)場互斥一般死鎖了厦瓢,不過有一點需要注意到提揍。

  • 構(gòu)造器注入的時候才會真正形成循環(huán)依賴,會提示錯誤
  • 如果是setting注入則不會報錯的煮仇,上面好像沒說具體的依賴類型劳跃,只是有個mbd.getDependsOn

回到過去看下如何取到getDependsOn數(shù)據(jù)的

BeanDefinitionParseDelegate 文件


public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, 
   String beanName,BeanDefinition containingBean, AbstractBeanDefinition bd) {
   .....
    if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
        String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
        // 支持多個數(shù)據(jù),用逗號分開
        bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
    }

很明顯上述代碼可以知道getDependsOn是bean的另一種屬性DEPENDS_ON_ATTRIBUTE設(shè)置的值浙垫,這是一種比ref更強制性依賴的配置刨仑,從spring3.0開始有的,會提前初始化夹姥,看代碼分析也確實是如何杉武,但是和我們理解的循環(huán)依賴貌似不是同一件事情,這點后面再說佃声。

繼續(xù)看如何生成object的

AbstractAutowireCapableBeanFactory 文件

protected Object createBean(String beanName, RootBeanDefinition mbd, 
        Object[] args) throws BeanCreationException {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;
    
    // 獲得當(dāng)前bean的的class艺智,并且為了嚴(yán)格加載當(dāng)前bean,生成一個新的對象mbdToUse
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }

    try {
        mbdToUse.prepareMethodOverrides();
        // 校驗當(dāng)前的bean使用overrides 重載方法是否合法(未重載則提示錯誤)
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                beanName, "Validation of method overrides failed", ex);
    }

    try {
        // 可以產(chǎn)生一個新的proxy代理去完成某些功能圾亏,也是一個接口
        // 需要去實現(xiàn)InstantiationAwareBeanPostProcessor類
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
    }

  // 來到了真正去創(chuàng)建一個普通的bean的地方了
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isDebugEnabled()) {
        logger.debug("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}

上面的代碼中介紹了InstantiationAwareBeanPostProcessor這個類,這個類也是BeanPostProcessor的一種封拧,代碼中這樣描述這個功能的Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.志鹃。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
        throws BeanCreationException {

    // 初始化一個bean的包裝類(貌似框架級別的包裝類都喜歡使用wrapper)
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
       // 生成bean實例
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    .....
}


protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 確認當(dāng)前處理的bean是需要被處理的bean
    Class<?> beanClass = resolveBeanClass(mbd, beanName);

    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }

    if (mbd.getFactoryMethodName() != null)  {
       // 存在工廠方法,用工廠方法去實現(xiàn)實例化的功能
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // Shortcut when re-creating the same bean...
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            return instantiateBean(beanName, mbd);
        }
    }

    // SmartInstantiationAwareBeanPostProcessor
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // 實例化對象
    return instantiateBean(beanName, mbd);
}

至此已經(jīng)生成了一個對象泽西,但是其并沒有相關(guān)屬性曹铃,需要填充數(shù)據(jù)了,繼續(xù)看doCreateBean方法

    BeanWrapper instanceWrapper = ......
    
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                // 依舊是對beanpostprocessor接口的對外處理捧杉,接口是MergedBeanDefinitionPostProcessor
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    // 已經(jīng)緩存的實例用來解決循環(huán)引用
    if (earlySingletonExposure) {
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });
    }

    // expose 對外輸出的對象
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        // 填充數(shù)據(jù)陕见,這一步就是完成數(shù)據(jù)從配置文件到對象屬性的設(shè)置
        if (exposedObject != null) {
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
        else {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    }
    

如下圖,最后是使用了AutowireAnnotationBeanPostProcessor 類去實現(xiàn)指的注入味抖,圈住的地方恰好可以把值"CS-2"塞入"className"中

image.png

最后可能需要對獲取到的object轉(zhuǎn)一下格式评甜,就完成了一整個的getBean的操作。

在這里有一點需要注意到仔涩,在解析xml生成beandefinition和調(diào)用getBean之間并沒有絕對的順序之分忍坷,并不是解析xml完成,再去生成對應(yīng)的實例的熔脂,在解析的時候如果需要具體的實例佩研,則會立即調(diào)用getBean

上面提的幾個問題有些答案或許已經(jīng)清楚了還有些可能不是非常清楚,下一篇文章再從比較宏觀的角度出發(fā)去了解下getBean是如何解決各種問題的以及注解的值具體是如何被掛載上去的霞揉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姑曙,一起剝皮案震驚了整個濱河市烤惊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖岗宣,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁猴,死亡現(xiàn)場離奇詭異刊咳,居然都是意外死亡娱挨,警方通過查閱死者的電腦和手機捕犬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門柴钻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贴届,“玉大人,你說我怎么就攤上這事元潘。” “怎么了耀鸦?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵洛姑,是天一觀的道長龄广。 經(jīng)常有香客問我蕴侧,道長敲才,這世上最難降的妖魔是什么阻星? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任已添,我火速辦了婚禮,結(jié)果婚禮上禾怠,老公的妹妹穿的比我還像新娘弦讽。我一直安慰自己,他們只是感情好往产,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布被碗。 她就那樣靜靜地躺著,像睡著了一般仿村。 火紅的嫁衣襯著肌膚如雪锐朴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天蔼囊,我揣著相機與錄音焚志,去河邊找鬼衣迷。 笑死,一個胖子當(dāng)著我的面吹牛酱酬,可吹牛的內(nèi)容都是我干的壶谒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼膳沽,長吁一口氣:“原來是場噩夢啊……” “哼汗菜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贵少,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤呵俏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后滔灶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體普碎,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年录平,在試婚紗的時候發(fā)現(xiàn)自己被綠了麻车。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡斗这,死狀恐怖动猬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情表箭,我是刑警寧澤赁咙,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站免钻,受9級特大地震影響彼水,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜极舔,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一凤覆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拆魏,春花似錦盯桦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至溪掀,卻和暖如春事镣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工璃哟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留氛琢,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓随闪,卻偏偏與公主長得像阳似,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铐伴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容