Spring Ioc之初始化

引言

spring加載xml的對(duì)象信息解析實(shí)例化成各個(gè)bean的過(guò)程我在這里就不細(xì)講了恨溜,畢竟從頭開(kāi)始看很容易繞暈大家苟穆,反而讓大家覺(jué)得這并不需要寫(xiě)。我們姑且認(rèn)為spring已經(jīng)加載好了各類(lèi)對(duì)象信息封裝成BeanDefinition骇陈,并已經(jīng)實(shí)例化存儲(chǔ)在了某個(gè)地方葵孤。不管是懶漢還是餓漢,都要經(jīng)歷反射出對(duì)象實(shí)例暇榴,然后初始化厚棵,我們先從spring中比較好理解的地方來(lái)入手IOC,那就是IOC中的bean在實(shí)例化之后的初始化操作蔼紧。主要涉及到BeanPostProcessor婆硬,InitializingBean這兩個(gè)的應(yīng)用。

IOC的bean初始化

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        invokeInitMethods(beanName, wrappedBean, mbd);

        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

為了方便閱讀奸例,我刪除了部分不相干的代碼彬犯。
AbstractAutowireCapableBeanFactory#initializeBean就是初始化IOC容器中的Bean的主要方法向楼,從這個(gè)方法入手,來(lái)看看IOC加載Bean到底做了什么谐区?
將實(shí)例化好的bean傳入該方法:

--> 調(diào)用BeanPostProcessor的postProcessBeforeInitialization方法
--> 調(diào)用bean實(shí)例的初始化方法
--> 調(diào)用BeanPostProcessor的postProcessAfterInitialization方法

以下三段代碼就是這三個(gè)過(guò)程:

@Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

循環(huán)所有實(shí)現(xiàn)了BeanPostProcessor類(lèi)的bean湖蜕,并執(zhí)行相應(yīng)的對(duì)象初始化之前的方法postProcessBeforeInitialization

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

該段代碼給一些實(shí)現(xiàn)了InitializingBean的bean進(jìn)行初始化操作。

注意:

1:spring為bean提供了兩種初始化的方式宋列,需要實(shí)現(xiàn)InitializingBean接口昭抒,重寫(xiě)afterPropertiesSet方法,或者在配置文件中同過(guò)init-method指定炼杖,兩種方式可以同時(shí)使用灭返,但是會(huì)先執(zhí)行afterPropertiesSet再執(zhí)行init-method。下文還有demo證明坤邪。

2:實(shí)現(xiàn)InitializingBean接口是直接調(diào)用afterPropertiesSet方法熙含,比通過(guò)反射調(diào)用init-method指定的方法效率相對(duì)來(lái)說(shuō)要高點(diǎn)。但是init-method方式消除了對(duì)spring的依賴(lài)

3:如果調(diào)用afterPropertiesSet方法時(shí)出錯(cuò)艇纺,則不調(diào)用init-method指定的方法怎静。

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

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

再次循環(huán)所有實(shí)現(xiàn)了BeanPostProcessor類(lèi)的bean,并執(zhí)行相應(yīng)的對(duì)象初始化之前的方法postProcessAfterInitialization喂饥。順便提一下消约,以后要寫(xiě)的Spring AOP的底層處理也是通過(guò)實(shí)現(xiàn)BeanPostProcessor來(lái)執(zhí)行代理包裝邏輯的。

IOC的bean注入

我們的ioc對(duì)象初始化好了员帮,接下來(lái)就要看看最關(guān)鍵的依賴(lài)注入了或粮。先思考一個(gè)問(wèn)題,spring在什么時(shí)候把對(duì)象注入進(jìn)去的捞高?這里先不解釋?zhuān)覀兛催@段源碼:

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        if (pvs instanceof MutablePropertyValues) {
            ...
        }
        else {
            original = Arrays.asList(pvs.getPropertyValues());
        }

        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

        // Create a deep copy, resolving any references for values.
        List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
        boolean resolveNecessary = false;
        for (PropertyValue pv : original) {
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            else {
                String propertyName = pv.getName();
                Object originalValue = pv.getValue();
                //在這里解析并塞入了注入的對(duì)象
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                Object convertedValue = resolvedValue;
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                if (convertible) {
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                // Possibly store converted value in merged bean definition,
                // in order to avoid re-conversion for every created bean instance.
                if (resolvedValue == originalValue) {
                    if (convertible) {
                        pv.setConvertedValue(convertedValue);
                    }
                    deepCopy.add(pv);
                }
                else if (convertible && originalValue instanceof TypedStringValue &&
                        !((TypedStringValue) originalValue).isDynamic() &&
                        !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                    pv.setConvertedValue(convertedValue);
                    deepCopy.add(pv);
                }
                else {
                    resolveNecessary = true;
                    deepCopy.add(new PropertyValue(pv, convertedValue));
                }
            }
        }
    }

我還是刪了很多其他邏輯氯材,這里主要的一句話(huà)就是Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
在這里分析并塞入了注入對(duì)象的關(guān)聯(lián)。具體怎么操作還得看自己去翻閱硝岗。接下來(lái)我用最簡(jiǎn)化的代碼方式來(lái)展示我的尋找過(guò)程:

AbstractBeanFactory類(lèi)
@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

我們肯定找到拿到每個(gè)bean的如果氢哮,如果不存在就會(huì)創(chuàng)建。

AbstractBeanFactory類(lèi)
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {
    // Create bean instance.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                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 = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
}

然后再進(jìn)入createBean()型檀,它的實(shí)現(xiàn)是在 AbstractAutowireCapableBeanFactory 當(dāng)中:

AbstractAutowireCapableBeanFactory類(lèi)
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    return beanInstance;
}
AbstractAutowireCapableBeanFactory類(lèi)
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }           
}

重點(diǎn)關(guān)注 createBeanInstance() 和 populateBean() 這兩個(gè)方法冗尤。其中,createBeanInstance方法生成了Bean所包含的Java對(duì)象:

AbstractAutowireCapableBeanFactory類(lèi)
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            return instantiateBean(beanName, mbd);
        }
    }
}
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
    BeanWrapper bw = new BeanWrapperImpl(beanInstance);
    initBeanWrapper(bw);
    return bw;
}

重點(diǎn)關(guān)注 getInstantiationStrategy()這個(gè)方法胀溺,可以看到instantiateBean方法的功能實(shí)現(xiàn)是通過(guò)調(diào)用getInstantiationStrategy().instantiate方法實(shí)現(xiàn)的裂七。 getInstantiationStrategy方法的作用是獲得實(shí)例化的策略對(duì)象,也就是指通過(guò)哪種方案進(jìn)行實(shí)例化的過(guò)程仓坞。繼續(xù)跟蹤下去我們可以發(fā)現(xiàn)背零,Spring當(dāng)中提供了兩種實(shí)例化方案: BeanUtils和Cglib方式。BeanUtils實(shí)現(xiàn)機(jī)制是通過(guò)Java的反射機(jī)制无埃,Cglib是一個(gè)第三方類(lèi)庫(kù)采用的是一種字節(jié)碼加強(qiáng)方式機(jī)制徙瓶。Spring中采用的默認(rèn)實(shí)例化策略是Cglib毛雇。

接下來(lái)就是重頭戲建立bean的依賴(lài)關(guān)系了。我們回到doCreateBean方法中的populateBean(beanName, mbd, instanceWrapper);

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    applyPropertyValues(beanName, mbd, bw, pvs);
}

有沒(méi)有發(fā)現(xiàn)侦镇,我們已經(jīng)來(lái)到我開(kāi)始講的applyPropertyValues方法了灵疮?
下面我們就不看源碼了,我們用反射來(lái)把value注入進(jìn)去虽缕,這樣更容易理解始藕。

try {
    Method declaredMethod = bean.getClass().getDeclaredMethod(
            "set" + propertyValue.getName().substring(0, 1).toUpperCase()
                    + propertyValue.getName().substring(1), value.getClass());
    declaredMethod.setAccessible(true);
    declaredMethod.invoke(bean, value);
} catch (NoSuchMethodException e) {
    Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
    declaredField.setAccessible(true);
    declaredField.set(bean, value);
}

拿到set參數(shù)的方法蒲稳,如果出現(xiàn)異常氮趋,表示并沒(méi)有寫(xiě)set方法,則就粗暴的方法塞入field江耀。(此處并不是源碼剩胁,是我根據(jù)源碼理解寫(xiě)的比較通俗易懂的方式)

這是我覺(jué)得在繁多的spring源碼中找出一段目標(biāo)代碼先看到,更容易讓人能夠跟著思路走下去祥国。

題外話(huà):

synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean =
                this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            dependenciesForBean.add(canonicalName);
        }

在看IOC源碼的時(shí)候看到Map的computeIfAbsent讓我恍然大悟昵观,我來(lái)用以前怎么寫(xiě)這段代碼的來(lái)解釋這段代碼什么意思如下:

Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean != null){
    dependenciesForBean = new LinkedHashSet<>(8);
}
dependenciesForBean.add(dependentBeanName);
dependenciesForBean.add(canonicalName);

這是jdk1.8才支持的lambda寫(xiě)法,是不是代碼更簡(jiǎn)潔舌稀?

再來(lái)分享一種簡(jiǎn)潔的方法啊犬,計(jì)算每個(gè)學(xué)生的總分記錄到map中:

List<Student> students = new ArrayList<>();
students.add(new Student("張三", "語(yǔ)文", 18));
students.add(new Student("張三", "數(shù)學(xué)", 20));
Map<String, Integer> resultMap = new HashMap<>();
for (Student student : students) {
    resultMap.merge(student.getName(), student.getScore(), (a, b) -> b + a);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市壁查,隨后出現(xiàn)的幾起案子觉至,更是在濱河造成了極大的恐慌,老刑警劉巖睡腿,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件语御,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡席怪,警方通過(guò)查閱死者的電腦和手機(jī)应闯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)挂捻,“玉大人碉纺,你說(shuō)我怎么就攤上這事】倘觯” “怎么了骨田?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)疫赎。 經(jīng)常有香客問(wèn)我盛撑,道長(zhǎng),這世上最難降的妖魔是什么捧搞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任抵卫,我火速辦了婚禮狮荔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘介粘。我一直安慰自己殖氏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布姻采。 她就那樣靜靜地躺著雅采,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慨亲。 梳的紋絲不亂的頭發(fā)上婚瓜,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音刑棵,去河邊找鬼巴刻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蛉签,可吹牛的內(nèi)容都是我干的胡陪。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼碍舍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柠座!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起片橡,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妈经,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后锻全,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體狂塘,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年鳄厌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荞胡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡了嚎,死狀恐怖泪漂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歪泳,我是刑警寧澤萝勤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站呐伞,受9級(jí)特大地震影響敌卓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伶氢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一趟径、第九天 我趴在偏房一處隱蔽的房頂上張望瘪吏。 院中可真熱鬧,春花似錦蜗巧、人聲如沸掌眠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蓝丙。三九已至,卻和暖如春望拖,著一層夾襖步出監(jiān)牢的瞬間渺尘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工靠娱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沧烈,地道東北人掠兄。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓像云,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蚂夕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迅诬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

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