在阅读Spring源码的过程中,会发现Spring框架中有许多不同类型的PostProcessor,今天整理一下Spring框架中有哪些PostProcessor,又分别起到了什么作用?
简单的来说,PostProcessor就是一种特殊的bean,它可以拦截bean的创建过程,以提供一些额外的处理。PostProcessor的主要作用是在Spring容器初始化bean时,允许我们介入bean的创建过程,以实现一些自定义的逻辑。
具体来说,PostProcessor可以在以下几个方面发挥作用:
BeanFactoryPostProcessor
接口,我们可以在Spring容器加载bean定义之后,但在实例化bean之前,对bean定义进行修改。例如,我们可以修改bean的作用域,或者添加新的属性值。BeanPostProcessor
接口,我们可以在Spring容器实例化bean之后,但在返回bean之前,对bean进行额外的处理。例如,我们可以修改bean的属性,或者返回一个完全不同的bean实例。BeanPostProcessor
的子接口,例如InstantiationAwareBeanPostProcessor
或DestructionAwareBeanPostProcessor
,我们可以在bean实例化之前、之后或销毁之前,对特定类型的bean进行更详细的处理。PropertyPlaceholderConfigurer
或PropertySourcesPlaceholderConfigurer
,我们可以在bean定义中使用占位符,并在实例化bean时,用实际的值替换这些占位符。总的来说,PostProcessor提供了一种强大的机制,使我们可以在Spring容器管理bean的生命周期的不同阶段,进行自定义的处理。这对于实现一些高级的功能,例如AOP(面向切面编程),事务管理,安全性等非常有用。接下来也会慢慢用实例讲解PostProcessor的作用。
Spring框架中有许多不同类型的PostProcessor,它们在Spring容器初始化bean时提供了一种机制,允许我们介入bean的创建过程。以下是一些常见的PostProcessor:
这是最常见的PostProcessor,它允许我们在Spring容器实例化bean之后,但在返回bean之前,对bean进行额外的处理。例如,我们可以修改bean的属性,或者返回一个完全不同的bean实例。先来看看源码:
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
这个接口只有两个默认的方法,通过方法的名字也可以很容易猜到这两个方法分别是在Bean的初始化前和初始化后调用。再看看它的实现也是特别的多。
现在用一个例子看看,我写了一个MyBeanPostProcessor
类实现了BeanPostProcessor
接口,要注意给这个类添加@Component
注解,才会被扫描成为一个Bean。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后");
return bean;
}
}
给这两个方法打上断点,看看这两个方法分别是什么时候调用的?
来看看postProcessBeforeInitialization
初始化之前和postProcessAfterInitialization
初始化之后这两个方法:
可以看到初始化之前和初始化之后都是是在doCreateBean方法里面,并且是在populateBean方法之后,这个时候Bean已经实例化完成,并完成好了属性填充也就是依赖注入。
postProcessBeforeInitialization
:这个方法在Bean的初始化方法(例如,标记为@PostConstruct的方法,或者实现InitializingBean接口的afterPropertiesSet方法)被调用之前执行。你可以在这个方法中进行一些预处理操作,例如修改Bean的属性,或者对Bean进行一些验证。postProcessAfterInitialization
:这个方法在Bean的初始化方法被调用之后执行。你可以在这个方法中进行一些后处理操作,例如包装Bean实例,或者返回一个完全不同的Bean实例。例如,你可以创建一个BeanPostProcessor,在postProcessAfterInitialization方法中,返回一个代理对象。这个代理对象可以在调用原始Bean的方法前后,执行一些额外的操作。这是实现AOP(面向切面编程)的一种常见方式。下面写了两个方法的基本使用
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof UserService){
((UserService) bean).name = "Echo";
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println("Before method:"+method.getName());
// Object result = method.invoke(bean,args);
// System.out.println("After method:"+method.getName());
// return result;
// }
// });
if(bean instanceof User){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((MethodInterceptor)(obj, method, args, proxy)->{
return proxy.invokeSuper(obj,args);
});
return enhancer.create();
}
return bean;
}
}
这里要注意如果使用JDK动态代理的话,被代理的对象必须要实现了接口。如果想要被代理对象没有实现接口的话,可以改成后面的CGLIB代理。
这个PostProcessor
允许我们在Spring容器实例化任何其他bean之前自定义应用程序上下文的bean定义。这是一个非常强大的特性,因为它允许我们在Spring框架的bean实例化阶段之前修改应用程序上下文。
BeanFactoryPostProcessor
的方法在Spring IoC容器的启动过程中被调用。具体来说,它们在所有的bean定义被加载到容器中之后,但在任何bean实例被创建之前被调用。这使得BeanFactoryPostProcessor
可以对bean定义进行修改,例如改变bean的属性或是改变bean的依赖。
以下是BeanFactoryPostProcessor的主要方法:
postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
:BeanFactoryPostProcessor
接口的唯一方法,它允许我们在Spring容器实例化任何其他bean之前自定义应用程序上下文的bean定义。在Spring的生命周期中,BeanFactoryPostProcessor
的方法运行的顺序如下:
BeanFactoryPostProcessor
。BeanFactoryPostProcessor
的postProcessBeanFactory
方法。因此,可以看出BeanFactoryPostProcessor
的方法在Spring IoC容器的启动过程中非常早就被调用,这使得它可以对bean定义进行大量的自定义操作。
示例
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("userService");
bd.getPropertyValues().addPropertyValue("name","keaizp");
}
}
这里要注意,当调用postProcessBeanFactory
方法,只是对BeanDefinition进行修改,Bean还没有进行实例化,真正的赋值是在属性填充的最后一步。
并且使用addPropertyValue方法,必须该属性要有setter方法,不然会报错。它正是利用setter方法赋值的。
看到这里也会想到上面说的BeanPostProcessor
也有一个postProcessBeforeInitialization
方法同样可以给属性赋值。不过一个是给Bean对象的属性赋值,一个是给BeanDefinition的属性赋值,如果两个方法都给同一个属性赋值会怎么样呢?通过它们在Spring生命周期的运行顺序可以很容易知道,只有属性会是最后哪个给Bean对象的属性赋的值,也就是postProcessBeforeInitialization
方法赋的值。
这是BeanPostProcessor的一个子接口,它提供了更多的回调方法,允许我们在bean实例化之前和之后,对bean进行更详细的处理。
先来看看它的源码:
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}
}
可以看到这个接口是继承了上面说到的BeanPostProcessor接口的,并且它是多了三个方法,分别是postProcessBeforeInstantiation
、postProcessAfterInstantiation
、postProcessPropertyValues
。前两个方法可以从名字很容易得知,分别是实例化之前和实例化之后运行。先来看看它们的什么时候调用。
先来看看postProcessBeforeInstantiation
方法的调用时间。
从这个图片可以看到这个是在createBean方法中,并且是在doCreateBean方法前,此时还没有实例化Bean。那这个方法有什么用处呢,能做些什么呢?
可以在这个方法中改变要实例化的 Bean 类,或者直接返回一个已经准备好的 Bean 实例,这样 Spring 容器就不会再去创建这个 Bean 了。如果这个方法返回非 null 的对象,那么后续的 BeanPostProcessor
将不会调用 postProcessBeforeInstantiation
方法,所以这里返回的对象,没办法在实例化后方法进行处理。并且从上面的源码可以看到,如果实例化前返回了一个非null对象,就不会执行后面的doCreateBean方法了,会直接返回bean。所以这里可以返回一个代理对象来替代原来要实例化的Bean。但是如果这个方法返回非 null 的对象,会直接执行初始化后的方法。
实例化之前和初始化之后这两个方法都能对bean进行代理,而且都会执行,那这两个方法都代理同一个对象会发生什么呢?
答案是会报错,而不是初始化之后的代理对象覆盖实例化前的代理对象,因为相当于初始化后将实例化之前的代理对象再进行代理一遍。
那实例化前和初始化后这两个方法进行代理有什么区别呢?
再来看看postProcessAfterInstantiation
方法是什么时候调用的
从这里可以看出postProcessAfterInstantiation
方法调用是在populateBean
方法中,此时刚进这个方法,还没进行属性填充,所以这个方法是在Bean实例化之后,但是是在属性填充之前。从代码也可以看出,如果实例化之后这个方法返回的是false,会直接return,就不会执行后面的填充方法了。所以postProcessAfterInstantiation
方法可以用来检查Bean的状态,如果不满足条件,可以阻止属性填充。
最后再来看postProcessPropertyValues
方法的调用
可以看到这个方法也是在populateBean
方法中,并且这个方法是在实例化之后的。这个方法正是用来给属性赋值的。
这个接口还有一个重要的实现类AutowiredAnnotationBeanPostProcessor
正是实现依赖注入的关键类,它实现了postProcessProperties
方法。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 找注入点(所有被@Autowired注解了的Field或Method)
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
Spring会执行所有InstantiationAwareBeanPostProcessor
的postProcessProperties
方法,这个时候有一个问题,如果自定义了一个InstantiationAwareBeanPostProcessor
实现类,并使用postProcessProperties
方法给pvs赋值,同时依赖注入也会给属性赋值,那该Bean的属性最后得到的是哪一个值呢?
这是populateBean
方法的最后的代码,可以看到最后pvs的值还是会覆盖依赖注入的值。
这是BeanPostProcessor的一个子接口,它提供了一个回调方法,允许我们在bean销毁之前,进行一些清理工作。
同样先看看源码:
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
default boolean requiresDestruction(Object bean) {
return true;
}
}
可以看到这个接口同样也是继承了BeanPostProcessor接口的,并且多了两个方法postProcessBeforeDestruction
和requiresDestruction
,并且requiresDestruction
是有自己的默认实现的。
postProcessBeforeDestruction(Object bean, String beanName)
: 这个方法在Spring容器销毁Bean之前被调用。你可以在这个方法中执行任何必要的清理工作,比如关闭网络连接,释放资源等。这个方法接收两个参数,第一个参数是要被销毁的Bean,第二个参数是该Bean的名称。接下来先来看看requiresDestruction
的调用堆栈
可以看到这个方法是在doCreateBean
方法的最后调用,结合上面的实例化前方法如果返回非null的对象,后面的doCreateBean方法不会再执行,可以知道:如果在实例化前方法里面代理一个Bean,将不会调用这个方法,无法判断这个Bean是否需要执行销毁回调,那这个Bean在销毁的时候也不会调用postProcessBeforeDestruction
方法。
再来看看postProcessBeforeDestruction的调用堆栈
这个方法的调用时机是在Spring容器关闭阶段,当容器关闭时,首先会检查是否存在DestructionAwareBeanPostProcessor
,如果存在,就会调用其postProcessBeforeDestruction
方法。然后,如果bean实现了DisposableBean
接口,就会调用其destroy
方法。
这是BeanFactoryPostProcessor的一个子接口,它提供了一个回调方法,允许我们在bean定义合并之后,对合并的bean定义进行处理。
先来看看这个接口的源码:
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
default void resetBeanDefinition(String beanName) {
}
}
可以看到这个接口同样是实现了BeanPostProcessor接口,有两个自己的方法postProcessMergedBeanDefinition
和resetBeanDefinition
,并且resetBeanDefinition
是有着一个空实现的。
再来看看postProcessMergedBeanDefinition
方法的调用堆栈
可以看到这个方法是在doCreateBean
方法中调用的,这个时候已经创建了Bean实例了,但是还没有进行属性填充,是在populateBean
方法之前的。这个方法可以用于修改合并后的 bean 定义,例如可以修改 bean 的属性值或者其他元数据。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if(beanType == UserService.class){
beanDefinition.getPropertyValues().add("name","zengpei");
}
}
这个接口还有一个重要的实现AutowiredAnnotationBeanPostProcessor
,这个实现在上面说过,也就是说InstantiationAwareBeanPostProcessor
的时候,它实现了InstantiationAwareBeanPostProcessor
和MergedBeanDefinitionPostProcessor
两个接口。先看看它的postProcessMergedBeanDefinition
方法。
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
}
从这里可以看到这里寻找@Autowire
注入点的过程。这个方法会在属性填充之前执行,也就是还没属性填充之前,就会先找到注入点并缓存起来。
最后写一个实例,展示一下前面的顺序,这里写了三个PostProcessor,都是用来给userService的name赋值,最后得到的结果是什么呢?
BeanFactoryPostProcessor
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("userService");
bd.getPropertyValues().addPropertyValue("name","keaizp");
}
}
BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前");
if(bean instanceof UserService){
((UserService) bean).name = "Echo";
}
return bean;
}
}
InstantiationAwareBeanPostProcessor
@Component
public class MyInstantiationPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println("postProcessProperties");
if(bean instanceof UserService){
MutablePropertyValues propertyValues;
if(pvs instanceof MutablePropertyValues){
propertyValues = (MutablePropertyValues) pvs;
}else {
propertyValues = new MutablePropertyValues(pvs);
}
if(propertyValues.contains("name")){
String name = (String)pvs.getPropertyValue("name").getValue();
propertyValues.addPropertyValue("name",name.toUpperCase());
}
return propertyValues;
}
return null;
}
}
MergedBeanDefinitionPostProcessor
@Component
public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
System.out.println("postProcessMergedBeanDefinition");
if(beanType == UserService.class){
beanDefinition.getPropertyValues().add("name","zengpei");
}
}
}
如果前面搞懂了,就会知道了应该是Echo
,postProcessBeforeInitialization
是初始化之前的方法,应该是最后执行的,执行的顺序依次是:postProcessBeanFactory
、postProcessMergedBeanDefinition
、postProcessProperties
、postProcessBeforeInitialization
。
所以name变化的顺序应该是:"keaizp"
-> "zengpei"
-> "ZENGPEI"
-> "Echo"
,当然前面三个改变的都是pvs,只有最后的实例化之前,改变的是Bean对象的值。