前言:在【spring源码分析】IOC容器初始化(八)中多次提到了前置处理与后置处理,本篇文章针对此问题进行分析。Spring对前置处理或后置处理主要通过BeanPostProcessor进行实现。


BeanPostProcessor的作用:在Bean实例化前后,如果需要对Bean进行一些配置、增加一些自己的处理逻辑,则使用BeanPostProcessor。

BeanPostProcessor示例

定义一个类实现BeanPostProcessor接口:

 public class UserDefinedBeanPostProcessor implements BeanPostProcessor {

     @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanName=" + beanName + " 初始化之前进入");
if ("beanPostProcessorBase".equals(beanName)) {
BeanPostProcessorBase processorBase = (BeanPostProcessorBase) bean;
processorBase.setMsg("Hello BeanPostProcessor!!!!!!!");
}
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanName=" + beanName + " 初始化之后进入");
return bean;
} public void showMsg() {
System.out.println("BeanPostProcessor Test!!!!!!");
}
}

再定义一个基础测试类:

 public class BeanPostProcessorBase {

     private String msg;

     public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}

测试方法:

     @Test
public void beanPostProcessorTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("com/dev/config/beanpostprocessor/beanpostprocessor.xml");
BeanPostProcessorBase postProcessor = context.getBean(BeanPostProcessorBase.class);
System.out.println(postProcessor.getMsg());
}

运行结果:

分析:

首先必须明确BeanPostProcessor的执行时机:AbstractAutowireCapableBeanFactory#initializeBean方法

 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 安全模式
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
// 激活Aware方法,对特殊bean处理:Aware、BeanClassLoaderAware、BeanFactoryAware
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
} else {
// 非安全模式下激活Aware方法,对特殊bean处理:Aware、BeanClassLoaderAware、BeanFactoryAware
invokeAwareMethods(beanName, bean);
} // 后置处理器 before
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);
}
// 后置处理器 after
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}

分析:

  • 从该函数逻辑可以看出,BeanPostProcessor的执行时机是在bean初始化前后,其实这里也可以看出bean的生命周期,这里后面再分析。
  • 还有为什么要创建一个基础测试类来进行演示,如果直接使用UserDefinedBeanPostProcessor类进行演示是得不到我们所需要的效果的,因为ApplicationContext对象会自动注册BeanPostProcessor,并且在注册会实例化BeanPostProcessor,然后在第二次调用的时候就会从单例缓存中取值,因此得不到想要的效果。自动注册并实例化BeanPostProcessor参看PostProcessorRegistrationDelegate#registerBeanPostProcessors函数,细读该函数就会豁然开朗。

BeanPostProcessor总结

BeanPostProcessor可以理解为是Spring的一个工厂钩子(其实Spring提供一系列的钩子,如Aware 、InitializingBean、DisposableBean),它是Spring提供的对象实例化阶段强有力的扩展点,允许Spring在实例化bean阶段对其进行定制化修改,比较常见的使用场景是处理标记接口实现类或者为当前对象提供代理实现(例如 AOP)。

BeanPostProcessor的执行时机如下:

注意:一般的BeanFactory是不支持自动注册BeanPostProcessor,需手动调用addBeanPostProcessor进行注册,但是ApplicationContext支持自动注册,但是在其注册过程中就会对BeanPostProcessor进行初始化并进缓存,因此在示例代码中利用了基础测试类来进行演示。

BeanPostProcessor的作用域是容器级别的,它只和所在的容器相关,当BeanPostProcessor完成注册后,它会应用于所有跟它在同一容器内的bean


by Shawn Chen,2019.05.05,上午。

最新文章

  1. Jetty+Xfire 嵌入式webService应用实践
  2. 学习微信小程序之css8
  3. mysql 添加索引 mysql 创建索引
  4. GridView控件RowDataBound事件的一个实例
  5. UVALive 5905 Pool Construction 最小割,s-t割性质 难度:3
  6. 50. Pow(x, n)
  7. 一道C语言面试题:写一个宏,将16位的整数转为Big Endian
  8. 微信小程序,前端大梦想(一)
  9. js数组及数组应用(冒泡和二分,遍历输出)
  10. hdu 4630 查询[L,R]区间内任意两个数的最大公约数
  11. ffmpeg推送直播流的技术进展
  12. 关于MySQL 8.0的几个重点【转】
  13. 019_nginx upstream中keepalive参数
  14. WARNING: Can not get binary dependencies for file...
  15. 直流-直流(DC-DC)变换电路_BUCK&amp;BOOST变换电路
  16. MD5与SHA散列单项加密
  17. L2-008 最长对称子串 (25 分)
  18. axaj 的回调
  19. DES/3DES/AES区别
  20. C#编程(二十四)----------修饰符

热门文章

  1. 字蛛webfont 安装及使用方法
  2. js如何获取数值
  3. Java 面向对象(五)抽象
  4. Vscode ftp
  5. Spring中Bean的管理问题
  6. 雨后清风U盘启动盘的五大用处及制作方法
  7. git 从存储库中删除敏感数据(删除文件历史)
  8. 用js刷剑指offer(重建二叉树)
  9. contos 6.6下安装lamp
  10. 类对象传输到jsp页面。需要转换为js的json对象时,这么做。