一、接口描述
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
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
根据以上描述,我们可以看到原有推断有一些问题,两个方法的执行主要是在bean完成初始化之后,准备执行默认方法时候对bean进行包装。
三、应用场景
几个典型的应用如:
1、解析bean的注解,将注解中的字段转化为属性
2、统一将属性在执行前,注入bean中,如数据库访问的sqlMap,如严重服务,这样不需要每个bean都配置属性
3、打印日志,记录时间等。
四、实践
1、定义接口和实例
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的配置
此处实例中需要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的值
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
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、由于两个方法执行的时间特殊性,所以打印日志和记录时间意义不大,主要还是用于注入属性和完善配置