Spring源碼解析(十一)-初始化bean

Spring版本

5.2.5.RELEASE

參考

《芋道源碼》

源碼解讀

1. AbstractAutowireCapableBeanFactory#initializeBean

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            // 通過aware接口進行一些set設置
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            // 初始化之前應用PostProcessors
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            // 應用init方法
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            // 初始化之后應用beanProcessors
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

主流程主要包含四個步驟:

  • invokeAwareMethods進行一些屬性的設置
  • applyBeanPostProcessorsBeforeInitialization應用BeanPostProcessorsapplyBeanPostProcessorsBeforeInitialization方法
  • invokeInitMethods應用初始化方法
  • applyBeanPostProcessorsAfterInitialization應用BeanPostProcessorsapplyBeanPostProcessorsAfterInitialization

2. AbstractAutowireCapableBeanFactory#invokeAwareMethods

    private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

Aware接口是spring提供的一種回調機制,這里簡單地設置了beanName忆家、beanClassLoader础淤、beanFactory的屬性鸟悴,關于Aware接口可戳// TODO

3. AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

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

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            // 應用每個PostProcessors
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

獲取到BeanPostProcessor集合,遍歷憋飞,應用每個BeanPostProcessorpostProcessBeforeInitialization方法咱圆,如果返回值為空,則返回上一次處理后的bean

4. AbstractAutowireCapableBeanFactory#invokeInitMethods

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

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                // afterPropertiesSet提供了一個機會在properties設置之后去改變properties
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                // 通過反射調用自定義初始化方法
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

做了倆件事:

  • 首先判斷bean是否是InitializingBean類型鲁森,如果是,應用afterPropertiesSet方法振惰,從方法名可以看出歌溉,該方法提供了一個在設置完畢property之后再去修改property的機會
  • 應用自定義的init-method方法

4.1 InitializingBean

public interface InitializingBean {

    /**
     * Invoked by the containing {@code BeanFactory} after it has set all bean properties
     * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
     * <p>This method allows the bean instance to perform validation of its overall
     * configuration and final initialization when all bean properties have been set.
     * @throws Exception in the event of misconfiguration (such as failure to set an
     * essential property) or if initialization fails for any other reason
     */
    void afterPropertiesSet() throws Exception;

}

InitializingBean接口僅包含afterPropertiesSet一個方法,下面通過一個小demo來展示其效果

4.1.1 demo

4.1.1.1 Teacher.java
public class Teacher implements InitializingBean {

    private String name;


    @Override
    public void afterPropertiesSet() throws Exception {
        name = "name set by afterPropertiesSet";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
4.1.1.2 spring.xml
    <bean id="teacher" class="com.kungyu.custom.element.Teacher">
        <property name="name" value="name set by property label"/>
    </bean>
4.1.1.3 測試
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        Teacher teacher = (Teacher) context.getBean("teacher");
        System.out.println(teacher.getName());

    }
}
4.1.1.4 結果
afterPropertiesSet

4.1.2 結論

可以看到骑晶,在spring.xml中痛垛,name屬性設置的name set by property label已經被afterPropertiesSet方法中的name set by afterPropertiesSet所覆蓋

4.2 init-method

對4.1中的demo稍加修改

4.2.1 demo

4.2.1.1 Teacher.java
public class Teacher implements InitializingBean {

    private String name;


    @Override
    public void afterPropertiesSet() throws Exception {
        name = "name set by afterPropertiesSet";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void initMethod() {
        this.name = "name set by init method";
    }
}
4.2.1.2 spring.xml
    <bean id="teacher" class="com.kungyu.custom.element.Teacher" init-method="initMethod">
        <property name="name" value="name set by property label"/>
    </bean>
4.2.1.3 結果
init-method

4.2.2 源碼解析

        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    // 如果init-method設置為afterPropertiesSet,那么前面已經運行過了透罢,沒必要再跑一遍
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                // 通過反射調用自定義初始化方法
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }

這里做了一個優(yōu)化榜晦,如果init-method設置為afterPropertiesSet方法冠蒋,那么其實4.1中已經應用了init-method羽圃,就無需再次運行了,而invokeCustomInitMethod方法的核心在于使用反射來應用init-method方法:

ReflectionUtils.makeAccessible(methodToInvoke);
methodToInvoke.invoke(bean);

5. AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

    @Override
    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;
    }

邏輯與第三點大同小異

總結

初始化bean整體邏輯相對于其他流程來說,可以說是簡單了很多朽寞,通過倆個小demo识窿,也更清晰地理解了源碼的執(zhí)行流程,沒有什么比實例來得更有說服力了脑融。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末喻频,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子肘迎,更是在濱河造成了極大的恐慌甥温,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓布,死亡現場離奇詭異姻蚓,居然都是意外死亡,警方通過查閱死者的電腦和手機匣沼,發(fā)現死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門狰挡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人释涛,你說我怎么就攤上這事加叁。” “怎么了唇撬?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵它匕,是天一觀的道長。 經常有香客問我窖认,道長超凳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任耀态,我火速辦了婚禮轮傍,結果婚禮上,老公的妹妹穿的比我還像新娘首装。我一直安慰自己创夜,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布仙逻。 她就那樣靜靜地躺著驰吓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪系奉。 梳的紋絲不亂的頭發(fā)上檬贰,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音缺亮,去河邊找鬼翁涤。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的葵礼。 我是一名探鬼主播号阿,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸳粉!你這毒婦竟也來了扔涧?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤届谈,失蹤者是張志新(化名)和其女友劉穎枯夜,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體艰山,經...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡卤档,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了程剥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劝枣。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖织鲸,靈堂內的尸體忽然破棺而出舔腾,到底是詐尸還是另有隱情,我是刑警寧澤搂擦,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布稳诚,位于F島的核電站,受9級特大地震影響瀑踢,放射性物質發(fā)生泄漏扳还。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一橱夭、第九天 我趴在偏房一處隱蔽的房頂上張望氨距。 院中可真熱鬧,春花似錦棘劣、人聲如沸俏让。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽首昔。三九已至,卻和暖如春糙俗,著一層夾襖步出監(jiān)牢的瞬間勒奇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工巧骚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赊颠,地道東北人格二。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像巨税,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子粉臊,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355