Spring 鉤子之BeanFactoryPostProcessor和BeanPostProcessor的源碼學習

BeanFactoryPostProcessor和BeanPostProcessor這兩個接口都是初始化bean時對外暴露的入口之一,和Aware類似(PS:關于spring的hook可以看看Spring鉤子方法和鉤子接口的使用詳解講的蠻詳細)本文也主要是學習具體的鉤子的細節,以便于實際開發中我們能有效率,例如如何在scala中如何獲取springboot的啟動類等等,一些中間件為了監控整個系統的服務,也需要獲取到spring容器數據和狀態。
接下來具體學習和了解下BeanFactoryPostProcessor和BeanPostProcessor。

BeanFactoryPostProcessor

bean工廠的bean屬性處理容器,說通俗一些就是可以管理我們的bean工廠內所有的beandefinition(未實例化)數據,可以隨心所欲的修改屬性。

使用方法

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    /**
     * 主要是用來自定義修改持有的bean
     * ConfigurableListableBeanFactory 其實就是DefaultListableBeanDefinition對象
     * @param beanFactory
     * @throws BeansException
     */

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("調用了自定義的BeanFactoryPostProcessor " + beanFactory);
        Iterator it = beanFactory.getBeanNamesIterator();

        String[] names = beanFactory.getBeanDefinitionNames();
        // 獲取了所有的bean名稱列表
        for(int i=0; i<names.length; i++){
            String name = names[i];

            BeanDefinition bd = beanFactory.getBeanDefinition(name);
            System.out.println(name + " bean properties: " + bd.getPropertyValues().toString());
            // 本內容只是個demo,打印持有的bean的屬性情況
        }
    }
}
<context:property-placeholder location="pro.properties" />
<bean id="student" class="Student" init-method="init" />
<bean class="CustomBeanFactoryPostProcessor" id="customBeanFactoryPostProcessor" />

輸出結果

image.png

源碼分析

毫無疑問肯定已經解析xml了,繼續看refresh函數

AbstractApplicationContext 文件

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // 解析xml完成,存儲在一個具體的bean工廠中
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // bean工廠的初始化操作
        prepareBeanFactory(beanFactory);
        try {
            // 由子類繼承去實現該類,當前該方法為空
            postProcessBeanFactory(beanFactory);
            // invoke 其中存在的BeanFactoryPostProcessors,也就是我們現在說的
            invokeBeanFactoryPostProcessors(beanFactory);
        ...

PostProcessorRegistrationDelegate 文件

invokeBeanFactoryPostProcessors方法的參數為bean工廠ConfigurableListableBeanFactory和當前已知的postprocessor對象,函數分為了好幾部分去處理,截取其中我們關心的部分即可(其實還包含了優先級、屬性等類似處理過程)

String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
        // 篩選出bean工程中存在的所有實現BeanFactoryPostProcessor類的類名稱

        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (processedBeans.contains(ppName)) {
                // 已經存在了,不再處理
            }
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
                // 為PriorityOrdered類型
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
                // 為Ordered類型
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
                // 這個就是我們當前需要關心的PostProcessors
            }
        }

        ...
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
            // 獲取自定義的BeanFactoryPostProcessor
            // 這里有一點需要注意到!!!!
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

上述代碼中nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));中使用了getBean,起初沒注意到以為是生成具體的對象然后修改,其實不是,getBean后面還有一個參數BeanFactoryPostProcessor.class,注意看這個函數,會發現返回的是一個抽象類,結論就是nonOrderedPostProcessors添加的不是bean實例,而是beandefinition,在實例化前!!!,這點很重要

private static void invokeBeanFactoryPostProcessors(
        Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanFactory(beanFactory);
        // 調用每一個自定義的BeanFactoryPostProcessor的方法
        // 在本文章中就會去調用自定義的CustomBeanFactoryPostProcessor的postProcessBeanFactory方法
    }
}

再多說一點關于上面的getBeanNamesForType函數,從名字肯定很容易理解了,根據傳遞的類型獲取容器中的beanName。了解下其內部的實現原理

DefaultListableBeanFactory 文件的 getBeanNamesForType函數

// type:類的類型名稱
// includeNonSingletons:返回數據包含了非單例beanName
// allowEagerInit: 可以提前加載初始化
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
       // 不可用緩存、類型無效、不允許提前加載初始化
       // 需要獲取當前type的原始類型,繼續獲取數據
        return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
    }
    Map<Class<?>, String[]> cache =
            (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
    String[] resolvedBeanNames = cache.get(type);
    // 如果緩存已經存儲了該數據,則無需再計算,直接返回即可
    if (resolvedBeanNames != null) {
        return resolvedBeanNames;
    }
    resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
    // 這一步就是真正的獲取數據,遍歷beanDefinitionNames的每一個數據,符合要求的就會加入到返回的列表中
    
    if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
        cache.put(type, resolvedBeanNames);
        // 便于下一次獲取,加入緩存中
    }
    return resolvedBeanNames;
}

BeanPostProcessor

從范圍上來說,從上面的所有的bean成為了特定的bean,其次BeanFactoryPostProcessor可以在初始化前修改bean的屬性等情況,但是BeanPostProcessor只能在初始化后(注意初始化不包括init方法)執行一些操作。
網上很多文章都說BeanPostProcessor不能修改bean屬性,其實我看來未必,當其實例化之后,完全可以拿到實例化后的對象,對對象進行一些改值操作也完全可以的

使用方法

public class Student {

    @Value("${name}")
    private String className;

    public Student() {
        System.out.println("constructor loading");
    }

    public void init(){
        System.out.println("init loading");
    }
}
public class CustomBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Student){
            // 如果當前的bean是Student,則打印日志
            System.out.println("postProcessBeforeInitialization bean : " + beanName);
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Student){
            System.out.println("postProcessAfterInitialization bean : " + beanName);
        }
        return bean;
    }
}
<bean id="student" class="Student" init-method="init" />
<bean class="CustomBeanPostProcessor" id="customBeanPostProcessor" />

輸出結果

image.png
  • 先打印出了構造器內部執行的話,意味著這個時候實例化了Student類,
  • 在init方法前執行了postProcessBeforeInitialization
  • 在init方法后執行了postProcessAfterInitialization

源碼分析

入口依舊是refresh函數,在完成初始化之后,進入到finishBeanFactoryInitialization(beanFactory)執行BeanPostProcessor的相關操作,中間的流程過長,包含了getBean操作,genBean操作過于繁瑣,后續再介紹。

image.png

AbstractAutowireCapableBeanFactory 文件

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
        // aware同樣是對外提供的鉤子
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // 這一步就是執行自定義的beanpostprocessor的before操作
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
        // 執行init方法
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        // 這一步就是執行自定義的beanpostprocessor的after操作
    }
    return wrappedBean;
}
public Object applyBeanPostProcessorsBeforeInitialization(
                    Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
       // 獲取所有的BeanPostProcessor對象,執行postProcessBeforeInitialization方法
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
    // 然后把執行結果返回
}
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;
}
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) {
       // invoke 反射執行init方法
        String initMethodName = mbd.getInitMethodName();
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容