spring 源碼之 Bean 的加載(一)

直接切入正題把夸,搞懂 Bean 的加載 spring 源碼就差不多了硅则,萬卷不離其宗症见。

AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(XXX.class);
         app.getBean(YYY.class);

直接通過 AnnotationConfigApplicationContext 就到達(dá)入口单芜。這個 app 可以 get bean 所以 bean 在第一行就創(chuàng)建好了细疚。

    public AnnotationConfigApplicationContext(Class... annotatedClasses) {
        this();
        this.register(annotatedClasses);
        this.refresh();
    }

refresh()
這個就是整個Spring Bean加載的核心了沐绒,它是ClassPathXmlApplicationContext的父類AbstractApplicationContext的一個方法俩莽,顧名思義,用于刷新整個Spring上下文信息乔遮,定義了整個Spring上下文加載的流程扮超。

public void refresh() throws BeansException, IllegalStateException {
        // 對象鎖
        synchronized(this.startupShutdownMonitor) {
            //準(zhǔn)備刷新上下文 (設(shè)置激活標(biāo)志位)
            this.prepareRefresh();
            //獲取刷新Spring上下文的Bean工廠
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //準(zhǔn)備 bean 工廠
            this.prepareBeanFactory(beanFactory);

            try {
                //允許 beanFactory 的 后置處理器
                this.postProcessBeanFactory(beanFactory);
                //調(diào)用工廠處理器注冊bean
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //注冊 bean 后置處理器
                this.registerBeanPostProcessors(beanFactory);
                //初始消息資源
                this.initMessageSource();
                //初始 event 多路廣播
                this.initApplicationEventMulticaster();
                //初始一些特殊的 bean
                this.onRefresh();
                //注冊監(jiān)聽器
                this.registerListeners();
                // 這里完成bean 的初始化(非懶加載)
                this.finishBeanFactoryInitialization(beanFactory);  
                // 發(fā)布相關(guān)事件
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
                //刪除 bean
                this.destroyBeans();
                //取消 refresh
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                // reset 激活標(biāo)志位
                this.resetCommonCaches();
            }

        }
    }

1、方法是加鎖的蹋肮,這么做的原因是避免多線程同時刷新Spring上下文出刷。

2、盡管加鎖可以看到是針對整個方法體的坯辩,但是沒有在方法前加 synchronized 關(guān)鍵字馁龟,而使用了對象鎖 startUpShutdownMonitor,這樣做有兩個好處:

(1)refresh()方法和close()方法都使用了 startUpShutdownMonitor 對象鎖加鎖漆魔,這就保證了在調(diào)用 refresh() 方法的時候無法調(diào)用 close() 方法坷檩,反之亦然却音,避免了沖突。

(2)另外一個好處不在這個方法中體現(xiàn)矢炼,但是提一下系瓢,使用對象鎖可以減小了同步的范圍,只對不能并發(fā)的代碼塊進(jìn)行加鎖句灌,提高了整體代碼運(yùn)行的效率夷陋。

finishBeanFactoryInitialization(beanFactory) 主要完成 bean 的加載。

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        if (beanFactory.containsBean("conversionService") && beanFactory.isTypeMatch("conversionService", ConversionService.class)) {
            beanFactory.setConversionService((ConversionService)beanFactory.getBean("conversionService", ConversionService.class));
        }

        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                public String resolveStringValue(String strVal) {
                    return AbstractApplicationContext.this.getEnvironment().resolvePlaceholders(strVal);
                }
            });
        }

        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        String[] var3 = weaverAwareNames;
        int var4 = weaverAwareNames.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String weaverAwareName = var3[var5];
            this.getBean(weaverAwareName);
        }

        beanFactory.setTempClassLoader((ClassLoader)null);
        beanFactory.freezeConfiguration();
        beanFactory.preInstantiateSingletons();
    }

preInstantiateSingletons 方法是初始化單例 bean 的方法:

    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }

        List<String> beanNames = new ArrayList(this.beanDefinitionNames);
        Iterator var2 = beanNames.iterator();

        while(true) {
            while(true) {
                String beanName;
                RootBeanDefinition bd;
                do {
                    do {
                        do {
                            if (!var2.hasNext()) {
                                var2 = beanNames.iterator();

                                while(var2.hasNext()) {
                                    beanName = (String)var2.next();
                                    Object singletonInstance = this.getSingleton(beanName);
                                    if (singletonInstance instanceof SmartInitializingSingleton) {
                                        final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
                                        if (System.getSecurityManager() != null) {
                                            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                                                public Object run() {
                                                    smartSingleton.afterSingletonsInstantiated();
                                                    return null;
                                                }
                                            }, this.getAccessControlContext());
                                        } else {
                                            smartSingleton.afterSingletonsInstantiated();
                                        }
                                    }
                                }

                                return;
                            }

                            beanName = (String)var2.next();
                            bd = this.getMergedLocalBeanDefinition(beanName);
                        } while(bd.isAbstract());
                    } while(!bd.isSingleton());
                } while(bd.isLazyInit());

                if (this.isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = (Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            public Boolean run() {
                                return ((SmartFactoryBean)factory).isEagerInit();
                            }
                        }, this.getAccessControlContext());
                    } else {
                        isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
                    }

                    if (isEagerInit) {
                        this.getBean(beanName);
                    }
                } else {
                    this.getBean(beanName);
                }
            }
        }
    }

這里先解釋一下getMergedLocalBeanDefinition方法的含義胰锌,因為這個方法會常称疲看到。Bean定義公共的抽象類是AbstractBeanDefinition资昧,普通的Bean在Spring加載Bean定義的時候爹谭,實例化出來的是GenericBeanDefinition,而Spring上下文包括實例化所有Bean用的AbstractBeanDefinition是RootBeanDefinition榛搔,這時候就使用getMergedLocalBeanDefinition方法做了一次轉(zhuǎn)化,將非RootBeanDefinition轉(zhuǎn)換為RootBeanDefinition以供后續(xù)操作东揣。

第1行~第10行的代碼是根據(jù)beanName拿到RootBeanDefinition践惑。由于此方法實例化的是所有非懶加載的單例Bean,因此要實例化Bean嘶卧,必須滿足11行的三個定義:

(1)不是抽象的

(2)必須是單例的

(3)必須是非懶加載的

接著簡單看一下第12行~第29行的代碼尔觉,這段代碼主要做的是一件事情:首先判斷一下Bean是否FactoryBean的實現(xiàn),接著判斷Bean是否SmartFactoryBean的實現(xiàn)芥吟,假如Bean是SmartFactoryBean的實現(xiàn)并且eagerInit(這個單詞字面意思是渴望加載侦铜,找不到一個好的詞語去翻譯,意思就是定義了這個Bean需要立即加載的意思)的話钟鸵,會立即實例化這個Bean钉稍。Java開發(fā)人員不需要關(guān)注這段代碼,因為SmartFactoryBean基本不會用到棺耍,我翻譯一下Spring官網(wǎng)對于SmartFactoryBean的定義描述:

-FactoryBean接口的擴(kuò)展接口贡未。接口實現(xiàn)并不表示是否總是返回單獨的實例對象,比如FactoryBean.isSingleton()實現(xiàn)返回false的情況并不清晰地表示每次返回的都是單獨的實例對象蒙袍。

-不實現(xiàn)這個擴(kuò)展接口的簡單FactoryBean的實現(xiàn)俊卤,F(xiàn)actoryBean.isSingleton()實現(xiàn)返回false總是簡單地告訴我們每次返回的都是單獨的實例對象,暴露出來的對象只能夠通過命令訪問害幅。

-注意:這個接口是一個有特殊用途的接口消恍,主要用于框架內(nèi)部使用與Spring相關(guān)。通常以现,應(yīng)用提供的FactoryBean接口實現(xiàn)應(yīng)當(dāng)只需要實現(xiàn)簡單的FactoryBean接口即可狠怨,新方法應(yīng)當(dāng)加入到擴(kuò)展接口中去

獲取Bean對象實例约啊,都是通過getBean方法,跟進(jìn)去看 doGetBean 方法取董。真正干事兒的基本上都是 do 開頭的方法棍苹。

protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
        '//提取對應(yīng)的 beanName'
        final String beanName = this.transformedBeanName(name);
        '// 檢查緩存中或者實例工廠中是否有對應(yīng)的實例
        // 這里是為了解決單例 bean 下的循環(huán)依賴 
        //spring 創(chuàng)建 bean 的原則是不等 bean 創(chuàng)建完就將 創(chuàng)建 bean 的 ObjectFactory 提早曝光。
        //也就是將 ObjectFactory 加入到緩存中'
        Object sharedInstance = this.getSingleton(beanName);
        Object bean;
        
        if (sharedInstance != null && args == null) {
            if (this.logger.isDebugEnabled()) {
                if (this.isSingletonCurrentlyInCreation(beanName)) {
                    this.logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
                } else {
                    this.logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            '// 返回對應(yīng)的實例茵汰,有時候存在諸如 BeanFactory 的情況并不是直接返回實例本身而是返回指定方法返回的實例(這里應(yīng)該是用了代理)'
            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
        } else {
            '// 只有單例情況下才會解決循環(huán)依賴'
            if (this.isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            BeanFactory parentBeanFactory = this.getParentBeanFactory();
           ' // 如果 beanDefinitionMap 中也就是在所有已經(jīng)加載的類中不包括 beanName 則嘗試從 parentBeanFactory 中檢測'
            if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
                String nameToLookup = this.originalBeanName(name);
                '// 遞歸到 BeanFactory 中尋找'
                if (args != null) {
                    return parentBeanFactory.getBean(nameToLookup, args);
                }

                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            '// 如果不是僅僅做類型檢查則是創(chuàng)建 bean 這里要記錄'
            if (!typeCheckOnly) {
                this.markBeanAsCreated(beanName);
            }

            try {
                '// 將存儲 XML 配置文件的 GernericBeanDefiniton 轉(zhuǎn)換為 RootBeanDefinitionm, 如果指定 BeanName 是子 Bean 的同時會合并父類的相關(guān)屬性'
                final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
                this.checkMergedBeanDefinition(mbd, beanName, args);
                String[] dependsOn = mbd.getDependsOn();
                String[] var11;
                '// 若存在依賴則需要遞歸實例化依賴的 bean'
                if (dependsOn != null) {
                    var11 = dependsOn;
                    int var12 = dependsOn.length;
                    
                    for(int var13 = 0; var13 < var12; ++var13) {
                        String dep = var11[var13];
                        if (this.isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        '// 緩存依賴調(diào)用'
                        this.registerDependentBean(dep, beanName);

                        try {
                            this.getBean(dep);
                        } catch (NoSuchBeanDefinitionException var24) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);
                        }
                    }
                }
                '// 實例化依賴的 bean 后便可以實例化 mbd本身了'
                if (mbd.isSingleton()) {
                    sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
                        public Object getObject() throws BeansException {
                            try {
                                return AbstractBeanFactory.this.createBean(beanName, mbd, args);
                            } catch (BeansException var2) {
                                AbstractBeanFactory.this.destroySingleton(beanName);
                                throw var2;
                            }
                        }
                    });
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                '// 原型模式的創(chuàng)建'
                } else if (mbd.isPrototype()) {
                    var11 = null;

                    Object prototypeInstance;
                    try {
                        this.beforePrototypeCreation(beanName);
                        prototypeInstance = this.createBean(beanName, mbd, args);
                    } finally {
                        this.afterPrototypeCreation(beanName);
                    }

                    bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                } else {
                    '// 指定的 scope 上實例化 bean'
                    String scopeName = mbd.getScope();
                    Scope scope = (Scope)this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }

                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            public Object getObject() throws BeansException {
                                AbstractBeanFactory.this.beforePrototypeCreation(beanName);

                                Object var1;
                                try {
                                    var1 = AbstractBeanFactory.this.createBean(beanName, mbd, args);
                                } finally {
                                    AbstractBeanFactory.this.afterPrototypeCreation(beanName);
                                }

                                return var1;
                            }
                        });
                        bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    } catch (IllegalStateException var23) {
                        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", var23);
                    }
                }
            } catch (BeansException var26) {
                this.cleanupAfterBeanCreationFailure(beanName);
                throw var26;
            }
        }
        '// 檢查需要的類型是否符合 bean 的實際類型'
        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
            try {
                return this.getTypeConverter().convertIfNecessary(bean, requiredType);
            } catch (TypeMismatchException var25) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);
                }

                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        } else {
            return bean;
        }
    }

加載過程中所涉及的步驟如下:
1.轉(zhuǎn)換對應(yīng) beanName
這里傳入的參數(shù)可能是別名枢里,也可能是 FactoryBean,所以需要進(jìn)行一系列的解析蹂午。
-去除 FactoryBean 的修飾符栏豺,如果 name 是 “&aa”,那么去除 & 使 name 為 “aa”。
-去指定的 alias 所表示的最終 beanName豆胸,例如別名 A 指向名稱為 B 的 bean 則返回 B奥洼。

2.嘗試從緩存中加載單例
單例在 Spring 的同一容器內(nèi)只會被創(chuàng)建一次,后續(xù)再獲取 bean晚胡,就直接從單例緩存中獲取了灵奖。這里只是嘗試加載,首先嘗試從緩存中加載估盘,如果加載不成功則再次嘗試從 singletonFactories 中加載瓷患。為了避免循環(huán)依賴,Spring 的原則是不等 bean 創(chuàng)建完成就會將創(chuàng)建 bean 的 ObjectFactory 提早曝光加入到緩存中遣妥。

  1. bean 的實例化
    如果從緩存中得到了 bean 的原始狀態(tài)擅编,則需要對 bean 進(jìn)行實例化。緩存中只是最原始的 bean 狀態(tài)箫踩,并不一定是想要的爱态。假如我們要對工廠 bean 進(jìn)行處理,這里其實是工廠 bean 的初始狀態(tài)境钟,但是我們真正需要的是工廠 bean 中定義的 factory-method 方法中返回的 bean锦担,而 getObjectForBeanInstance 就是完成這個工作的。

4.原型模式的依賴檢查
只有單例情況下才會嘗試解決循環(huán)依賴吱韭,原型模式下只會拋出異常

5.檢測 parentBeanFactory
parentBeanFactory != null && !this.containsBeanDefinition(beanName) 加載的 XML 配置文件中不包含 beanName 所對應(yīng)的配置 就只能到 parentBeanFactory 去嘗試下了吆豹,然后再去遞歸的調(diào)用 getBean 方法。

6.將存儲 XML 配置文件的 GernericBeanDefinition 轉(zhuǎn)換為 RootBeanDefinition

7.尋找依賴
bean 初始化過程中很可能用到某些屬性理盆,而某些屬性可能是動態(tài)配置的痘煤,并且配置成依賴于其他的 bean, 那么這個時候就有必要先加載依賴的 bean,所以猿规,在 spring 加載循序中衷快,在初始化 bean 的時候會首先初始化這個 bean 所對應(yīng)的依賴。

8.針對不用的 scope 進(jìn)行 bean 的創(chuàng)建姨俩。
singleton prototype request 等蘸拔。

9.類型轉(zhuǎn)換
將返回的 bean 轉(zhuǎn)換為 requiredType 所指定的類型师郑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市调窍,隨后出現(xiàn)的幾起案子宝冕,更是在濱河造成了極大的恐慌,老刑警劉巖邓萨,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件地梨,死亡現(xiàn)場離奇詭異,居然都是意外死亡缔恳,警方通過查閱死者的電腦和手機(jī)宝剖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歉甚,“玉大人万细,你說我怎么就攤上這事≈叫梗” “怎么了赖钞?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長聘裁。 經(jīng)常有香客問我仁烹,道長,這世上最難降的妖魔是什么咧虎? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮计呈,結(jié)果婚禮上砰诵,老公的妹妹穿的比我還像新娘。我一直安慰自己捌显,他們只是感情好茁彭,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扶歪,像睡著了一般理肺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上善镰,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天妹萨,我揣著相機(jī)與錄音,去河邊找鬼炫欺。 笑死乎完,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的品洛。 我是一名探鬼主播树姨,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摩桶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帽揪?” 一聲冷哼從身側(cè)響起硝清,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎转晰,沒想到半個月后芦拿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡挽霉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年防嗡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侠坎。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚁趁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出实胸,到底是詐尸還是另有隱情他嫡,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布庐完,位于F島的核電站钢属,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏门躯。R本人自食惡果不足惜淆党,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讶凉。 院中可真熱鬧染乌,春花似錦、人聲如沸懂讯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褐望。三九已至勒庄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘫里,已是汗流浹背实蔽。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留谨读,地道東北人盐须。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贼邓。 傳聞我的和親對象是個殘疾皇子阶冈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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