一、接口描述
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、由於兩個方法執行的時間特殊性,所以打印日志和記錄時間意義不大,主要還是用於注入屬性和完善配置
