一、接口描述

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、由于两个方法执行的时间特殊性,所以打印日志和记录时间意义不大,主要还是用于注入属性和完善配置

[spring源码学习]五-BeanPostProcessor的使用的更多相关文章

  1. spring源码学习五 - xml格式配置,如何解析

    spring在注入bean的时候,可以通过bean.xml来配置,在xml文件中配置bean的属性,然后spring在refresh的时候,会去解析xml配置文件,这篇笔记,主要来记录.xml配置文件 ...

  2. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  3. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

  4. 【目录】Spring 源码学习

    [目录]Spring 源码学习 jwfy 关注 2018.01.31 19:57* 字数 896 阅读 152评论 0喜欢 9 用来记录自己学习spring源码的一些心得和体会以及相关功能的实现原理, ...

  5. Spring 源码学习——Aop

    Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...

  6. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

  7. Spring 源码学习笔记11——Spring事务

    Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...

  8. spring源码学习之路---深入AOP(终)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...

  9. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

随机推荐

  1. sql中的inner join ,left join ,right join

    左连接LEFT JOIN, 也就是说,左外连接的含义是限制连接关键字右端的表中的数据必须满足连接条件,而不关左端的表中的数据是否满足连接条件,均输出左端表中的内容.不满足连接条件的 ,连接字段栏位将对 ...

  2. Web jquery表格组件 JQGrid 的使用 - 11.问题研究

    系列索引 Web jquery表格组件 JQGrid 的使用 - 从入门到精通 开篇及索引 Web jquery表格组件 JQGrid 的使用 - 4.JQGrid参数.ColModel API.事件 ...

  3. 面试题目——《CC150》数学与概率

    面试题7.2:三角形的三个顶点上各有一只蚂蚁.如果蚂蚁开始沿着三角形的边爬行,两只或三只蚂蚁撞到一起的概率有多大?假定每只蚂蚁会随机选一个方向,每个方向被选到的几率相等,而且三只蚂蚁的爬行速度相同. ...

  4. Sql Server自动备份数据库,定期删除备份

    //实现:每天自动备份数据库,定期删除备份 //步骤:[开始]--[所有程序]--[Microsoft SQL Server 2005]--[SQL Server Management Studio] ...

  5. Linux进程间通信(三):匿名管道 popen()、pclose()、pipe()、close()、dup()、dup2()

    在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值.这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据. 一.什 ...

  6. PF_INET 和 AF_INET 的区别

    在写网络程序的时候,建立TCP socket: sock = socket(PF_INET, SOCK_STREAM, 0); 然后再绑定本地地址或连接远程地址时需要初始化sockaddr_in结构, ...

  7. MySQL Cluster 数据分布(分区、分组)

    数据分布 1.MySQL Cluster自动分区数据表(也可能使用用户自定义分区),将数据分布到分区中: 2.一个数据表被划分到多个Data Node分区中,数据在分区中被”striped”: 3.主 ...

  8. /etc/rc.d/rc与/etc/rc.d/init.d的关系

    在这里先解释一下 /etc/rc.d/init.d 里面放的都是什么东西.这个目录存放的是一些脚本,一般是Linux以rpm包安装时设定的一些服务的启动/关闭脚本.系统在安装时装了好多rpm包,这里面 ...

  9. iOS应用动态部署方案

    iOS的动态部署能极大的节约成本.苹果的审核周期很长,有的时候,你可能不得不等待将近2个星期去上架你的新功能或者bug.所以动态部署是有价值的. 我这里讨论的情况不把纯web应用考虑在内,因为用户体验 ...

  10. UVa2521

    理解:max 记录的是有大牌的个数 mid 是有中断 而造成的不确定  我理解是一个间断点以下的 数和一个间断点抵消 在前面没有间断的情况下 才能确定这张牌稳赢 #include<iostrea ...