Spring IOC的深入理解(五)

引言

在執(zhí)行ac.getBean("car")的過程中,spring如何加載bean的?

FactoryBean

public interface FactoryBean<T>
{
//......
}

當配置文件<bean>中的class的類是FactoryBean接口的實現(xiàn)類時,getBean返回的就不是對應的類,而是這個實現(xiàn)類調(diào)用get
Object()方法所返回的對象,相當于是getObject()代理了getBean()類

從緩存中獲取單例類

單例在spring中只會創(chuàng)建一次,后續(xù)獲取bean直接從單例緩存中獲取,首先嘗試從緩存中加載,后序在嘗試從SingletonFactories中獲取.
Spring創(chuàng)建bean的原則就是在創(chuàng)建一個bean時候,不等bean創(chuàng)建完成就將ObjectFactory提早曝光到緩存中.下次有bean依賴到這個已經(jīng)創(chuàng)建的bean的時候,就直接使用ObjectFactory

Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

首先查看緩存中是否有實例,如果有就獲取
如果沒有的話,全局加鎖,在從earlySingletonObjects中獲取,如果還是獲取不到,就從singletonFactories中獲取BeanName對應的ObjectFactory,然后調(diào)用這個singletonFactory來創(chuàng)建singletonObject,然后放到earlySingletonObjects中去,然后從singletonFactories清除掉這個beanName.
其中有大量的map,我們來進行梳理一下:

  • singletonObjects bean和BeanName的關系如果其中有bean,就可以直接創(chuàng)建
  • singletonFactories BeanName和Bean工廠之間的關系
  • earlySingletonObjects BeanName和bean,主要是用來做循環(huán)檢驗的

這個時候我們就獲取到了beanName對應的bean了

從bean的實例中獲取對象

如果緩存不為空的話,我們就已經(jīng)獲取到實例了,記住這個時候我們是從緩存中獲取的實例,然后我們繼續(xù)回到最開始的方法

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 + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
我們進入到這個方法中
這個代碼大概的意思就是,嘗試從緩存中獲取加載bean,如果加載失敗,我們就可以明確的知道是Factorybean類型的,然后調(diào)用getObjectFromFactoryBean這個方法,其中doGetObjectFromFactoryBean

    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
            throws BeanCreationException {

        Object object;
        try {
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                object = factory.getObject();
            }
        }

object = factory.getObject();之后回到上一層代碼,會調(diào)用如下的一段代碼

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

這個就是后處理器的使用,在后面我們會詳細的介紹

創(chuàng)建單例

    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });

上面講的是從緩沖中獲取單例bean,然后從bean的實例中獲取對象.接下來就是如果緩存中沒有bean,我們該怎么辦,我們進入到這個重載的getSingleton中

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    // Has the singleton object implicitly appeared in the meantime ->
                    // if yes, proceed with it since the exception indicates that state.
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                }
                catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

其中singletonObject = singletonFactory.getObject();是初始化bean的,我們來看看在它之前都做了些什么?檢查和記錄
beforeSingletonCreation(beanName);和 afterSingletonCreation(beanName);都是用來記錄加載狀態(tài)的,最后將緩存假加入到緩存中,并刪除一些緩存記錄.
我們已經(jīng)了解了bean的邏輯架構,真正的bean加載過程是在傳入的匿名內(nèi)部類中實現(xiàn)的

sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }

我們來深入了解createBean方法.
首先是解析Class,然后是對override方法進行標記和驗證

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
try {
            mbdToUse.prepareMethodOverrides();
        }
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

applyBeanPostProcessorsBeforeInstantiation和applyBeanPostProcessorsAfterInitialization是對getSingleton方法中的后處理的調(diào)用,
bean在實例化前會有一個后處理器的調(diào)用,將AbstractBeanDefinition轉(zhuǎn)化成BeanWrapper,經(jīng)過處理這有可能是一個經(jīng)過處理的bean,而后處理的過程,就是將bean注冊的過程,當bean再次使用時,就不必在進行創(chuàng)建了

創(chuàng)建bean

然后我們進入到doCreateBean方法中,這里是常規(guī)的bean操作.
如果是單例,首先需要清除緩存.實例化bean,將BeanDefinition轉(zhuǎn)換為BeanWrapper.使用工廠方法,使用構造方法,使用默認構造方法

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市执泰,隨后出現(xiàn)的幾起案子针肥,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡榨惰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門静汤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琅催,“玉大人,你說我怎么就攤上這事虫给√俾眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵抹估,是天一觀的道長缠黍。 經(jīng)常有香客問我,道長药蜻,這世上最難降的妖魔是什么瓷式? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谷暮,結果婚禮上蒿往,老公的妹妹穿的比我還像新娘盛垦。我一直安慰自己湿弦,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布腾夯。 她就那樣靜靜地躺著颊埃,像睡著了一般蔬充。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上班利,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天饥漫,我揣著相機與錄音,去河邊找鬼罗标。 笑死庸队,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的闯割。 我是一名探鬼主播彻消,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宙拉!你這毒婦竟也來了宾尚?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谢澈,失蹤者是張志新(化名)和其女友劉穎煌贴,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锥忿,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡牛郑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缎谷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片井濒。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖列林,靈堂內(nèi)的尸體忽然破棺而出瑞你,到底是詐尸還是另有隱情,我是刑警寧澤希痴,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布者甲,位于F島的核電站,受9級特大地震影響砌创,放射性物質(zhì)發(fā)生泄漏虏缸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一嫩实、第九天 我趴在偏房一處隱蔽的房頂上張望刽辙。 院中可真熱鬧,春花似錦甲献、人聲如沸宰缤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慨灭。三九已至朦乏,卻和暖如春婚惫,著一層夾襖步出監(jiān)牢的瞬間烫止,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工蕊苗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留筹陵,地道東北人刽锤。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像朦佩,于是被迫代替她去往敵國和親姑蓝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355