Spring源码之PostProcessor解析

系列文章目录



前言

  在阅读Spring源码的过程中,会发现Spring框架中有许多不同类型的PostProcessor,今天整理一下Spring框架中有哪些PostProcessor,又分别起到了什么作用?


一、PostProcessor是什么

  简单的来说,PostProcessor就是一种特殊的bean,它可以拦截bean的创建过程,以提供一些额外的处理。PostProcessor的主要作用是在Spring容器初始化bean时,允许我们介入bean的创建过程,以实现一些自定义的逻辑。

二、PostProcessor的作用

具体来说,PostProcessor可以在以下几个方面发挥作用:

  1. 修改bean的定义:通过实现BeanFactoryPostProcessor接口,我们可以在Spring容器加载bean定义之后,但在实例化bean之前,对bean定义进行修改。例如,我们可以修改bean的作用域,或者添加新的属性值。
  2. 修改bean的实例:通过实现BeanPostProcessor接口,我们可以在Spring容器实例化bean之后,但在返回bean之前,对bean进行额外的处理。例如,我们可以修改bean的属性,或者返回一个完全不同的bean实例。
  3. 处理特定类型的bean:通过实现BeanPostProcessor的子接口,例如InstantiationAwareBeanPostProcessorDestructionAwareBeanPostProcessor,我们可以在bean实例化之前、之后或销毁之前,对特定类型的bean进行更详细的处理。
  4. 处理占位符:通过使用PropertyPlaceholderConfigurerPropertySourcesPlaceholderConfigurer,我们可以在bean定义中使用占位符,并在实例化bean时,用实际的值替换这些占位符。

总的来说,PostProcessor提供了一种强大的机制,使我们可以在Spring容器管理bean的生命周期的不同阶段,进行自定义的处理。这对于实现一些高级的功能,例如AOP(面向切面编程),事务管理,安全性等非常有用。接下来也会慢慢用实例讲解PostProcessor的作用。

三、Spring框架中有哪些PostProcessor呢

  Spring框架中有许多不同类型的PostProcessor,它们在Spring容器初始化bean时提供了一种机制,允许我们介入bean的创建过程。以下是一些常见的PostProcessor:

BeanPostProcessor

这是最常见的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已经实例化完成,并完成好了属性填充也就是依赖注入。

在这里插入图片描述
那这两个方法又有什么用呢?

  1. postProcessBeforeInitialization:这个方法在Bean的初始化方法(例如,标记为@PostConstruct的方法,或者实现InitializingBean接口的afterPropertiesSet方法)被调用之前执行。你可以在这个方法中进行一些预处理操作,例如修改Bean的属性,或者对Bean进行一些验证。
  2. 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代理。

BeanFactoryPostProcessor

  这个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的方法运行的顺序如下:

  1. 所有的bean定义加载到容器中
  2. 实例化BeanFactoryPostProcessor
  3. Spring IoC容器调用BeanFactoryPostProcessorpostProcessBeanFactory方法。
  4. 实例化其他所有的bean。

因此,可以看出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方法赋的值。

InstantiationAwareBeanPostProcessor

这是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接口的,并且它是多了三个方法,分别是postProcessBeforeInstantiationpostProcessAfterInstantiationpostProcessPropertyValues。前两个方法可以从名字很容易得知,分别是实例化之前和实例化之后运行。先来看看它们的什么时候调用。
先来看看postProcessBeforeInstantiation方法的调用时间。
在这里插入图片描述
从这个图片可以看到这个是在createBean方法中,并且是在doCreateBean方法前,此时还没有实例化Bean。那这个方法有什么用处呢,能做些什么呢?

  可以在这个方法中改变要实例化的 Bean 类,或者直接返回一个已经准备好的 Bean 实例,这样 Spring 容器就不会再去创建这个 Bean 了。如果这个方法返回非 null 的对象,那么后续的 BeanPostProcessor 将不会调用 postProcessBeforeInstantiation 方法,所以这里返回的对象,没办法在实例化后方法进行处理。并且从上面的源码可以看到,如果实例化前返回了一个非null对象,就不会执行后面的doCreateBean方法了,会直接返回bean。所以这里可以返回一个代理对象来替代原来要实例化的Bean。但是如果这个方法返回非 null 的对象,会直接执行初始化后的方法

在这里插入图片描述
实例化之前和初始化之后这两个方法都能对bean进行代理,而且都会执行,那这两个方法都代理同一个对象会发生什么呢?

答案是会报错,而不是初始化之后的代理对象覆盖实例化前的代理对象,因为相当于初始化后将实例化之前的代理对象再进行代理一遍。

那实例化前和初始化后这两个方法进行代理有什么区别呢?

  1. 在实例化之前创建代理对象:在这种情况下,代理对象是在 Bean 实例创建之前就已经创建好了。这意味着代理对象不能访问到 Bean的任何状态和信息,因为 Bean 实例此时还没有创建。这种方式主要用于改变 Bean 的实例化逻辑,例如创建一个完全不同的 Bean实例,或者创建一个代理对象来替代原始的 Bean 实例。
  2. 在初始化之后创建代理对象:在这种情况下,代理对象是在 Bean 实例已经创建并初始化完成后才创建的。这意味着代理对象可以访问到 Bean 的所有状态和信息,包括属性值,依赖关系等。这种方式主要用于在不改变原始 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会执行所有InstantiationAwareBeanPostProcessorpostProcessProperties方法,这个时候有一个问题,如果自定义了一个InstantiationAwareBeanPostProcessor实现类,并使用postProcessProperties方法给pvs赋值,同时依赖注入也会给属性赋值,那该Bean的属性最后得到的是哪一个值呢?
在这里插入图片描述
这是populateBean方法的最后的代码,可以看到最后pvs的值还是会覆盖依赖注入的值。

DestructionAwareBeanPostProcessor

这是BeanPostProcessor的一个子接口,它提供了一个回调方法,允许我们在bean销毁之前,进行一些清理工作。
同样先看看源码:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

	void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

	default boolean requiresDestruction(Object bean) {
		return true;
	}

}

可以看到这个接口同样也是继承了BeanPostProcessor接口的,并且多了两个方法postProcessBeforeDestructionrequiresDestruction,并且requiresDestruction是有自己的默认实现的。

  1. postProcessBeforeDestruction(Object bean, String beanName): 这个方法在Spring容器销毁Bean之前被调用。你可以在这个方法中执行任何必要的清理工作,比如关闭网络连接,释放资源等。这个方法接收两个参数,第一个参数是要被销毁的Bean,第二个参数是该Bean的名称。
  2. requiresDestruction(Object bean): 这个方法用于判断一个Bean是否需要执行销毁回调。如果这个方法返回true,那么postProcessBeforeDestruction方法将会被调用。这个方法接收一个参数,即要被判断的Bean。

接下来先来看看requiresDestruction的调用堆栈
在这里插入图片描述

在这里插入图片描述
可以看到这个方法是在doCreateBean方法的最后调用,结合上面的实例化前方法如果返回非null的对象,后面的doCreateBean方法不会再执行,可以知道:如果在实例化前方法里面代理一个Bean,将不会调用这个方法,无法判断这个Bean是否需要执行销毁回调,那这个Bean在销毁的时候也不会调用postProcessBeforeDestruction方法。

再来看看postProcessBeforeDestruction的调用堆栈
在这里插入图片描述

在这里插入图片描述
这个方法的调用时机是在Spring容器关闭阶段,当容器关闭时,首先会检查是否存在DestructionAwareBeanPostProcessor,如果存在,就会调用其postProcessBeforeDestruction方法。然后,如果bean实现了DisposableBean接口,就会调用其destroy方法。

MergedBeanDefinitionPostProcessor

这是BeanFactoryPostProcessor的一个子接口,它提供了一个回调方法,允许我们在bean定义合并之后,对合并的bean定义进行处理。
先来看看这个接口的源码:

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {

	void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

	default void resetBeanDefinition(String beanName) {
	}

}

可以看到这个接口同样是实现了BeanPostProcessor接口,有两个自己的方法postProcessMergedBeanDefinitionresetBeanDefinition,并且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的时候,它实现了InstantiationAwareBeanPostProcessorMergedBeanDefinitionPostProcessor
两个接口。先看看它的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");
		}

	}
}

如果前面搞懂了,就会知道了应该是EchopostProcessBeforeInitialization是初始化之前的方法,应该是最后执行的,执行的顺序依次是:postProcessBeanFactorypostProcessMergedBeanDefinitionpostProcessPropertiespostProcessBeforeInitialization
所以name变化的顺序应该是:"keaizp" -> "zengpei" -> "ZENGPEI" -> "Echo",当然前面三个改变的都是pvs,只有最后的实例化之前,改变的是Bean对象的值。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值