[spring源碼學習]五-BeanPostProcessor的使用

一、接口描述

spring提供了一個接口類-BeanPostProcessor,我們叫他:bean的加工器,應該是在bean的實例化過程中對bean做一些包裝處理,裏邊提供兩個方法

複製代碼
public interface BeanPostProcessor
{

    public abstract Object postProcessBeforeInitialization(Object obj, String s)
        throws BeansException;

    public abstract Object postProcessAfterInitialization(Object obj, String s)
        throws BeansException;
}
複製代碼

   根據類的名稱,我們可以猜測兩個接口方法的定義分別爲:

  1、在bean初始化之前執行

  2、在bean的初始化之後執行

  我們需要到找到spring源碼中執行兩個方法的代碼進行驗證,在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);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            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()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }
複製代碼

 applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization的具體代碼分別爲:  

複製代碼
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;
    }
複製代碼
複製代碼
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;
    }
複製代碼

  根據以上代碼,我們得知,在invokeInitMethods的執行前後,spring會分別調用所有的BeanPostProcessor,執行其中的方法,那麼invokeInitMethods的具體內容我們仍需要看下,發現此方法主要作用有兩個:1、判斷bean是否繼承了InitializingBean,如果繼承接口,執行afterPropertiesSet()方法,2、獲得是否設置了init-method屬性,如果設置了,就執行設置的方法

複製代碼
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);
            }
        }
    }
複製代碼

  根據以上描述,我們可以看到原有推斷有一些問題,兩個方法的執行主要是在bean完成初始化之後,準備執行默認方法時候對bean進行包裝。

三、應用場景

  幾個典型的應用如:

  1、解析bean的註解,將註解中的字段轉化爲屬性

  2、統一將屬性在執行前,注入bean中,如數據庫訪問的sqlMap,如嚴重服務,這樣不需要每個bean都配置屬性

  3、打印日誌,記錄時間等。

四、實踐

  1、定義接口和實例

  

package com.zjl.beanpostprocessor;

public interface DemoService {
    public void sayHello();
}
複製代碼
public class DemoServiceImpl implements DemoService,NameInit {
    String name;

    @Override
    public void sayHello() {
        System.out.println("hello "+name);
    }

    @Override
    public void setName(String name) {
        this.name=name;
    }
}
複製代碼

  2、定義bean的配置

        <bean id="demoService" class="com.zjl.beanpostprocessor.DemoServiceImpl">
        </bean>

  此處實例中需要name的值進行打印,但是我們bean中並沒有提供此屬性

  3、定義注入接口

public interface NameInit {
    public void setName(String name);
}

  4、定義一個BeanPostProcessor 實例,凡是繼承了NameInit的接口,均實例化,注入name值。此處定義接口一方面是要使用接口中提供的setName方法,另一方面減輕系統壓力,防止每個bean都進行注入。

複製代碼
/**
 * 針對繼承了接口的bean,注入name
 * @author lenovo
 * @time 2016年4月21日
 *
 */
public class NameBeanPostProcessor implements BeanPostProcessor {
    String name;

    public String getName() {
        return name;
    }

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

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof NameInit){
            ((NameInit)bean).setName(name);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}
複製代碼

  5、定義bean,注入name的值

        <bean id="nameBeanPostProcessor" class="com.zjl.beanpostprocessor.NameBeanPostProcessor">
            <property name="name" value="zhangsan"></property>
        </bean>

  6、定義另一個BeanPostProcessor ,僅打印日誌

複製代碼
public class LogBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("正在處理"+beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("已經處理完成"+beanName);
        return bean;
    }

}
複製代碼

  7、定義bean

        <bean id="logBeanPostProcessor" class="com.zjl.beanpostprocessor.LogBeanPostProcessor">
        </bean>

  8、測試類

  

複製代碼
public class BeanPostProcessorTest {
    public static void main(String[] args) {
        ApplicationContext context=new FileSystemXmlApplicationContext("beanpostprocessor.xml");
        DemoService demoService=(DemoService) context.getBean("demoService");
        demoService.sayHello();
    }
}
複製代碼

  9、測試結果爲

正在處理demoService
已經處理完成demoService
hello zhangsan

  總結:根據執行結果,再次驗證

  1、兩個方法均在bean實例化期間已經完成,

  2、name屬性是根據NameInit接口自動注入

  3、由於兩個方法執行的時間特殊性,所以打印日誌和記錄時間意義不大,主要還是用於注入屬性和完善配置

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章