您现在的位置是:亿华云 > 知识
一个特殊的 BeanPostProcessor
亿华云2025-10-05 23:09:58【知识】2人已围观
简介来源:江南一点雨关于 BeanPostProcessor 松哥之前已经写过好几篇文章和大家聊过了,不过之前聊的都是常规的 BeanPostProcessor 玩法,还有一个特殊的 BeanPostPr
来源:江南一点雨
关于 BeanPostProcessor 松哥之前已经写过好几篇文章和大家聊过了,个特不过之前聊的个特都是常规的 BeanPostProcessor 玩法,还有一个特殊的个特 BeanPostProcessor,今天松哥来和大家梳理一下。个特
1. BeanPostProcessor
先来回顾一下 BeanPostProcessor 接口的个特定义:
public interface BeanPostProcessor{
@Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{
returnbean;
}
@Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{
returnbean;
}
}
这里就是两个方法,理解这两个方法有一个大的个特前提,就是个特此时 Spring 容器已经通过 Java 反射创建出来 Bean 对象了,只不过在初始化这个 Bean 对象的个特时候,又提供了一些配置接口:
postProcessBeforeInitialization:这个是个特在 Bean 初始化之前触发,此时我们已经有一个 Bean 对象了,个特但是个特 Bean 中一些生命周期方法如 InitializingBean 接口的 afterPropertiesSet 方法、自定义的个特 init-method 方法等都尚未执行,在这些方法执行之前触发 postProcessBeforeInitialization 方法。个特postProcessAfterInitialization:类似于上面,个特在 afterPropertiesSet 和自定义的个特 init-method 之后触发该方法。BeanPostProcessor 的应用非常广泛,在整个 Spring 体系中,也扮演了非常重要的角色,亿华云计算如 @Bean 注解的解析、AOP 动态代理的生成等等许多我们日常使用的功能,都是通过 BeanPostProcessor 来实现的。
2. MergedBeanDefinitionPostProcessor
MergedBeanDefinitionPostProcessor 算是整个 BeanPostProcessor 家族中比较另类的一个接口了,它虽然是 BeanPostProcessor,但是却可以处理 BeanDefinition。MergedBeanDefinitionPostProcessor 介入的时机就是 Bean 创建成功之后,Bean 中各个属性填充之前。
MergedBeanDefinitionPostProcessor 用于在 Bean 定义合并之后对合并后的 Bean 进行后置处理。它的作用是允许开发者在 Bean 定义合并完成后,对合并后的 Bean 进行自定义的修改或扩展操作。通常情况下,这个接口用于处理带有注解的 Bean 定义,例如 @Autowired 或 @Value 等注解的处理。通过实现 MergedBeanDefinitionPostProcessor 接口,云南idc服务商开发者可以在 Bean 定义合并后,对这些注解进行解析和处理,以实现自定义的逻辑。
来看下 MergedBeanDefinitionPostProcessor 接口:
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor{
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
default void resetBeanDefinition(String beanName){
}
}
这里就两个方法,一个是处理合并后的 BeanDefinition,还有一个是重置 Bean 的。
关于 MergedBeanDefinitionPostProcessor 接口有一个误区,有的小伙伴看到 merge 这个单词,又联想到松哥之前讲的给 bean 设置 parent(Spring BeanDefinition 也分父子?),会误以为合并 parent 属性是在这里完成的,其实这两个东西八杆子打不着。这里的单词也不是 merge,而是 merged,意思是处理合并之后的 BeanDefinition,而不是去进行 BeanDefinition 的合并。所以 MergedBeanDefinitionPostProcessor 并非进行 BeanDefinition 的合并处理,服务器租用而是在 BeanDefinition 合并完成之后,Bean 创建完毕之后,Bean 属性填充之前,做一些事情。
3. 场景分析
MergedBeanDefinitionPostProcessor 最为经典的使用场景是对于 @Autowired 注解的处理。
要理解这一点,小伙伴们先来看一下松哥画的这个 Spring 中 Bean 的创建流程图:

上图基本上涵盖了整个 Bean 的创建流程了,在 Bean 的创建流程中,有一个步骤是 populateBean,这个就是去填充 Bean 的,本质上就是给 Bean 的属性填充值。
小伙伴要问了,即然 populateBean 方法是给 Bean 的属性填充值的,那么通过 @Autowired 注解给 Bean 的属性注入值按理说也应该是在这个方法中完成的吧?为什么又要去到 MergedBeanDefinitionPostProcessor 中去完成呢?
其实这两个并不冲突。
在具体执行过程中,MergedBeanDefinitionPostProcessor 首先负责去将类中的各种被注解标记的方法和属性都找出来,然后进行处理,将处理结果封装为一个 InjectionMetadata 对象,然后缓存起来。
然后在 populateBean 为 Bean 填充属性的时候,直接去处理这些封装好的 InjectionMetadata。
以上就是大致逻辑。
我们再从源码角度来验证一下。
首先 Bean 的创建是在 AbstractAutowireCapableBeanFactory#doCreateBean 方法中进行的,我们来看下这个方法中几个关键步骤:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException{
//... synchronized(mbd.postProcessingLock) {
if(!mbd.postProcessed) {
try{
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
mbd.markAsPostProcessed();
}
}
//... try{
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//...}
在 applyMergedBeanDefinitionPostProcessors 方法中就会触发 MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition 方法的执行。以处理 @Autowired 注解为例,在 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition 方法中先进行属性的整理:
@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName){
findInjectionMetadata(beanName, beanType, beanDefinition);
}
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition){
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
returnmetadata;
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs){
// Fall back to class name as cache key, for backwards compatibility with custom callers.String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if(InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if(InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
returnmetadata;
}
可以看到,上面方法在执行的过程中会把查找到封装好的 InjectionMetadata 对象先给缓存起来。
接下来在 populateBean 方法中进行 Bean 的属性填充的时候,调用的 AutowiredAnnotationBeanPostProcessor#postProcessProperties 方法:
@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName){
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try{
metadata.inject(bean, beanName, pvs);
}
catch(BeanCreationException ex) {
throwex;
}
catch(Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
returnpvs;
}
此时再去调用 findAutowiringMetadata 方法的时候,就可以直接从缓存中获取 InjectionMetadata 了。
对于 InjectionMetadata#inject 方法以及 findAutowiringMetadata 方法,松哥在之前的文章中都已经详细介绍过了,这里就不再赘述了,不了解的小伙伴戳这里:@Autowired 到底是怎么把变量注入进来的?。
4. 小结
好了,这就是松哥和大家分享的 Spring 中一个特殊的 BeanPostProcessor -> MergedBeanDefinitionPostProcessor,特殊之处在于它和普通的 BeanPostProcessor 的执行时机不同。
很赞哦!(171)
相关文章
- .com域名是国际最广泛流行的通用域名,目前全球注册量第一的域名,公司企业注册域名的首选。国际化公司通常会注册该类域名。
- 手写简易前端框架:Function 和 Class 组件
- 面试突击:为什么ConcurrentHashMap是线程安全的?
- 联合体在单片机编程中的应用
- 在此期间,他们每天仍在这里卖大米,在理财方面个人感情有待提高。因为现在是收米的最佳时机。
- 微服务故障排除方面的优秀实践
- 冷门知识点:进程间通信如何加锁?
- MIT开发Twist编程语言:专门解决量子计算数据纠缠问题
- 网站页面结构改版,仅是页面样式发生变化,不会对排名、收录有影响;只有涉及到页面URL改变,才会对网站排名、收录有影响。
- MySQL 全局锁、表级锁、行级锁,你搞清楚了吗?
热门文章
站长推荐
付款完成后,您只需耐心等待,如果您注册成功,系统会提示您。这里需要注意的是,域名是一个即时产品,只有在最终付款成功时才能预订,注册成功后不能更改。
苹果 Safari 浏览器 16.1 新增支持通行密钥、Web 通知、Apple Pencil 悬停体验等
如何为 Node.js 的 Require 函数添加钩子?
2022年现代Python编程的四个关键点
公司名字不但要与其经营理念、活动识别相统一,还要能反映公司理念,服务宗旨、商品形象,从而才能使人看到或听到公司的名称就能产生愉快的联想,对商店产生好感。这样有助于公司树立良好的形象。
PostgreSQL 的并行框架
9张图,32个案例带你轻松玩转Java Stream
记一起由Oracle心跳引起的生产库故障