转自: https://www.jianshu.com/p/d26e8ec9c077?

BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。BeanPostProcessor的源码如下

public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

其中postProcessBeforeInitialization方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization方法会在每个bean对象的初始化方法调用之后被回调。具体执行时期可以参考Spring中Bean的生命周期源码探究

自定义BeanPostProcessor

查看BeanPostProcessor源码,可以看到它两个方法的参数都相同,其中第一个参数Object bean表示当前正在初始化的bean对象。此外两个方法都返回Object类型的实例,返回值既可以是将入参Object bean原封不动的返回出去,也可以对当前bean进行包装再返回。来看看下面的自定义BeanPostProcessor

/**
* 后置处理器:初始化前后进行处理工作
* 需要将后置处理器加入到容器中
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor { @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..."+beanName+"..."+bean);
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..."+beanName+"..."+bean);
return bean;
} }

Spring容器中加入MyBeanPostProcessor之后,针对容器中每个创建的Bean对象(Spring自身创建的Bean和应用程序创建的Bean),都会回调postProcessBeforeInitializationpostProcessAfterInitialization方法。

执行原理

BeanPostProcessor的执行是定义在容器的刷新过程中,容器刷新对象具体的方法为:AbstractApplicationContext.refresh()
在refresh方法执行的调用栈中会去调用AbstractAutowireCapableBeanFactory.doCreateBean()方法,该方法节选源码如下

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

    if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} invokeInitMethods(beanName, wrappedBean, mbd); if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}

看到在调用初始化方法前后会分别调用applyBeanPostProcessorsBeforeInitialization()applyBeanPostProcessorsAfterInitialization()applyBeanPostProcessorsBeforeInitialization()方法的源码如下

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
//获取所有的BeanPostProcessor进行遍历
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}

可以看到其逻辑为遍历得到容器中所有的BeanPostProcessor,然后一次执行postProcessBeforeInitialization,一但返回null,就跳出for循环不执行后面的BeanPostProcessor.postProcessorsBeforeInitialization()。也就是说如果返回的是null那么我们通过getBean方法将得不到目标Bean。
applyBeanPostProcessorsBeforeInitialization()方法的逻辑和上面一致,就是将循环执行的beanProcessor.postProcessBeforeInitialization()替换成beanProcessor.postProcessAfterInitialization()
Spring底层的很多功能特性都是借助BeanPostProcessor的子类来实现。

常见BeanPostProcessor分析

下图是debug过程中,ApplicationContext对象中的包含的BeanPostProcessor。具体包含哪些BeanPostProcessor和具体应用程序相关,除了下标3中的MyBeanPostProcessor为自定义的BeanPostProcessor,其余均为Spring自带的BeanPostProcessor。

 

下面来分析一下ApplicationContextAwareProcessorAutowiredAnnotationProcessor的执行原理。

ApplicationContextAwareProcessor

ApplicationContextAwareProcessor后置处理器的作用是,当应用程序定义的Bean实现ApplicationContextAware接口时注入ApplicationContext对象。

@Component
public class Car implements ApplicationContextAware { private ApplicationContext applicationContext; public Car(){
System.out.println("car instance...");
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware...setApplicationContext()");
this.applicationContext = applicationContext;
}
}

那Car是如何通过实现ApplicationContextAware接口就能获得ApplicationContext对象呢?答案是通过ApplicationContextAwareProcessor后置处理器来实现,我们来看看ApplicationContextAwareProcessor的源码

class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ConfigurableApplicationContext applicationContext;

    private final StringValueResolver embeddedValueResolver;

    /**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
} @Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
// 这里bean是Car,它实现了ApplicationContextAware接口
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
invokeAwareInterfaces(bean);
} return bean;
} private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
// 会执行这里回调car重写的setApplicationContext方法,然后将this.applicationContext注入给Car
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
} }

InitDestroyAnnotationBeanPostProcessor

InitDestroyAnnotationBeanPostProcessor后置处理器是用来处理自定义的初始化方法和销毁方法。Spring中提供了3种自定义初始化和销毁方法:

  1. 通过@Bean指定init-method和destroy-method属性
  2. Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑);
  3. @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法@PreDestroy:在容器销毁bean之前通知我们进行清理工作

InitDestroyAnnotationBeanPostProcessor的作用就是让第3种方式生效。先看看如何使用@PostConstruct@PreDestroy注解。

@Component
public class Car { public Car(){
System.out.println("car instance...");
} /**
* 自定义的初始化方法
*/
@PostConstruct
public void init(){
System.out.println("car ... init...");
} /**
* 自定义的销毁方法
*/
@PreDestroy
public void detory(){
System.out.println("car ... detory...");
}
}

InitDestroyAnnotationBeanPostProcessor会在Bean创建的时候通过反射的方式查找包含@PostConstruct@PreDestroy注解的方法,然后再通过反射执行方法。我们来看看InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization()的源码

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 获取bean的metadata
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 执行@PostConstruct指定的init方法
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}

metadata中已经解析出initMethods和destroyMethods,其具体内容如下。

 

metadata.invokeInitMethods(bean, beanName);就是根据反射执行init方法。

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessorBeanPostProcessor的子接口。

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    //实例化Bean之前调用
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException; //实例化Bean之后调用
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException; //设置Bean对象中的某个属性时调用
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException; }

这两个接口的功能基本类似,不过要注意InstantiationAwareBeanPostProcessorBeanPostProcessor的方法名区别。InstantiationAwareBeanPostProcessor中是Instantiation, 而BeanPostProcessor是Initialization。

BeanPostProcessor详解的更多相关文章

  1. Spring中的BeanPostProcessor详解

    Spring中的BeanPostProcessor详解 概述 BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初 ...

  2. 简单SSM配置详解

    SSM:spring+springMVC+Mybatis 学习网友的http://www.cnblogs.com/invban/p/5133257.html,并对其进行了详细的解说. 源码下载:htt ...

  3. [转载] 多图详解Spring框架的设计理念与设计模式

    转载自http://developer.51cto.com/art/201006/205212_all.htm Spring作为现在最优秀的框架之一,已被广泛的使用,51CTO也曾经针对Spring框 ...

  4. 10分钟详解Spring全家桶7大知识点

    Spring框架自2002年诞生以来一直备受开发者青睐,它包括SpringMVC.SpringBoot.Spring Cloud.Spring Cloud Dataflow等解决方案.有人亲切的称之为 ...

  5. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  6. SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架

    1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...

  7. [转载]Spring配置文件详解一:

    原文地址:与base-package="com.xx">Spring配置文件详解一:<context:annotation-config/>与<contex ...

  8. Spring MVC配置文件的三个常用配置详解

    转自:http://www.cnblogs.com/benwu/articles/5162614.html Spring MVC项目中通常会有二个配置文件,sprng-servlet.xml和appl ...

  9. Spring中的注入方式 和使用的注解 详解

    注解:http://www.cnblogs.com/liangxiaofeng/p/6390868.html 注入方式:http://www.cnblogs.com/java-class/p/4727 ...

随机推荐

  1. Oracle存储结构-表空间

    表空间 oracle文件注意的问题 控制文件,redolog文件,数据文件----一定要放在存储上 问题:银行用户,集群切换失败,原因是数据库中部分的文件未放到存储上 存储三种组织形式:文件系统.AS ...

  2. VS的快捷操作

    连按两下Tab,生成代码块.修改i为n,再按一次Tab,对应位置自动改变. Ctrl+. 或者 Alt+Enter ctor  连按两下Tab,生成无返回值的构造函数(constructor func ...

  3. 简单的尝试下angr

    0x00:前言 之前接触到了符号执行,可以用于程序的自动化分析,感觉还是比较神奇,工业上的具体用法不是很清楚,不过在CTF中这个东西慢慢在流行...从defcon 2016就可以看出(有很多人解re用 ...

  4. Confluence 6 评论一个文件

    无论是一个图片 —— 例如一个模拟的新市场计划需要反馈,还是一个 PDF 文件,一个演讲稿,或者任何你可以在 Confluence 中预览的文件.你可以在预览的的任何位置放置一个热点然后添加你的评论后 ...

  5. codevs 2924 数独挑战 x(三种做法+超详细注释~)

    2924 数独挑战  时间限制: 1 s  空间限制: 1000 KB  题目等级 : 钻石 Diamond   题目描述 Description “芬兰数学家因卡拉,花费3个月时间设计出了世界上迄今 ...

  6. [kuangbin带你飞]专题一 简单搜索 x

    A - 棋盘问题 POJ - 1321 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋 ...

  7. 32位linux安装chrome浏览器

    首先你需要一个安装包,可以在CSDN上搜索google-chrome-stable_current_i386.deb. 然后在终端输入 sudo apt-get install gdebi 然后找到安 ...

  8. 实现同时将一批.bmp文件转换成.mat格式

    %% 功能:实现同时对一批.bmp文件的转换成.mat格式PicFormat = {'*.bmp','Bitmap image (*.bmp)';... '*.jpg','JPEG image (*. ...

  9. canvas小实验

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 关于vue-resource 跨域请求的异常处理方法

    当你启动一个vue项目时,项目会运行在一个webpack的服务上,所以此时去访问其他端口或者是其他地址时,属于跨域请求,故会报异常. has been blocked by CORS policy: ...