【小白的Spring源码手册】 BeanFactoryPostProcessor的注册和用法(BFPP)

news2024/11/22 9:28:05

目录

  • 前言
  • 应用
    • 1. 手动注册
    • 2. 自动注册
    • 3. 优先级


前言

沿用上一篇文章的流程图,我们的注解类应用上下文中的AnnotationConfigApplicationContext#scan(String...)方法已经将所有BeanDefinition注册到了IoC容器中。完成注册后,开始执行AbstractApplicationContext#refresh()方法,这个方法中Spring向外部提供了几个非常重要的扩展点:BeanFactoryPostProcessor、BeanPostProcessor。他们的作用如下:

  • BeanFactoryPostProcessor:简称BFPP,用于管理BeanDefinition,甚至是管理整个IoC容器,常见业务实现有指定限定符(@Qualifier)、占位符替换(@Value)、配置类注册定义(@Configuration)等等;
  • BeanPostProcessor:简称BPP,用于管理Bean生命周期,常见业务实现有初始化(@PostConstruct)、注入依赖(@Autowired)、代理与包装Bean对象注册事件监听器销毁(@PreDestroy)、以及其它生命周期内事件处理等等。

在这里插入图片描述

学习和理解这两类处理器,能让你了解很多业务中常用功能的实现原理,看过之后就会恍然大悟,哦!原来我们日常业务使用的注解原来是这么回事。


是后处理器,还是后置处理器?Post Processor直译过来就是后处理器或者后置处理器的意思,两者意思大致相同,网上两个词也都在用。虽然这些处理器接口也并未声明它属于某些行为的后置处理器;也没有相对应的前置处理器(PreProcessor),但就总体流程和接口中方法的注释来看,BeanFactoryPostProcessor#postProcessBeanFactory就是IoC容器初始化流程的后置处理器,用来处理容器初始化后的事件;BeanPostProcessor#postProcessBeforeInitialization、BeanPostProcessor#postProcessAfterInitialization这两个方法则是Bean生命周期(BeanPostProcessor有很多实现类,用于分别处理各个阶段)中初始化前后的后置处理器,关于Bean生命周期后面会发文专门学习探讨一下。


综合来看,怎么称呼都没问题,不过我会在下文中都用后处理器来称呼。


其它Spring源码文章:
Bean的扫描、装配和注册,面试学习可用
BeanFactory后处理器的注册与执行——BeanFactoryPostProcessor(BFPP)
Bean后处理器的注册与执行——BeanPostProcessor(BPP)





应用

关于BFPP的类型:

BeanFactoryPostProcessor:提供postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法,在IoC容器标准初始化完成后,修改IoC容器内容,支持修改Bean定义、允许覆盖或添加属性。
BeanDefinitionRegistryPostProcessor:是BeanFactoryPostProcessor的子类,添加了一个新方法postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry),这个方法则更强调在IoC容器标准初始化完成后,执行postProcessBeanFactory方法之前,注册额外的Bean定义,这些新注册的内容同样会受postProcessBeanFactory方法影响。

1. 手动注册

所有实现BeanFactoryPostProcessor类的BFPP,都可以在上下文容器执行refresh方法前,通过AbstractApplicationContext#addBeanFactoryPostProcessor方法手动注册。

  AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  ctx.addBeanFactoryPostProcessor(new BfppA());
  ctx.addBeanFactoryPostProcessor(new BfppB());
  // 手动注册需确保注册必须在执行refresh方法前注册
  ctx.refresh();

手动注册的方式在Spring Boot启动类中比较常见,很多功能都是通过手动注册BFPP的方式来实现的。对Spring Boot感兴趣的,可以去看看SpringBoot的启动类(org.springframework.boot.SpringApplication),它在初始化应用上下文时,添加了很多默认的BFPP类。

// org.springframework.boot.SpringApplication#prepareContext
if (this.lazyInitialization) {
	context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));



2. 自动注册

也可以通过添加组件注解(如@Component)由IoC容器来自动注册后处理器,Spring会自动管理已经注册的所有BFPP类。

实现自动注册也很简单,添加组件注解即可:

@Component
public class SimpleBfPostProcessor implements BeanDefinitionRegistryPostProcessor, Ordered {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    	// 注册BeanDefinition等相关内容
    	RootBeanDefinition rbd = new RootBeanDefinition(ConfigBeanServiceImpl.class);
        registry.registerBeanDefinition("beanName", rbd);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 修改beanFactory内容,一般用于修改BeanDefinition的内容
        String propertyName = "name";
        BeanDefinition bd = beanFactory.getBeanDefinition("beanName");
		MutablePropertyValues propertyValues = bd.getPropertyValues();
		if (!propertyValues.contains(propertyName)) {
    		propertyValues.add(propertyName, "BeanFactoryPostProcessor-Property-Value");
		}
    }
    @Override
    public int getOrder() {
    	// 执行的优先级序号
        return Ordered.LOWEST_PRECEDENCE;
    }
}

在配置类中注入Bean和组件注解的作用相同:

@Configuration
public class PostProcessorConfig {
    @Bean
    public static SimpleBfPostProcessor simpleBfPostProcessor() {
        return new SimpleBfPostProcessor();
    }
}

有趣的是,配置类@Configuration的扫描和注册Bean的功能就是通过BeanDefinitionRegistryPostProcessor来实现的。所以你可以在配置类中使用@Bean@ComponentScans等等注解来实现注册Bean定义的功能。相关实现可以参考org.springframework.context.annotation.ConfigurationClassPostProcessor源码。



3. 优先级

看过源码的都会注意到,BFPP其实是有执行优先级的,根据源码,我把优先级分为如下几种类型:

提示:
interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
interface PriorityOrdered extends Ordered

  1. 根据注册方式:手动注册 > 自动注册
  2. 根据注册顺序:先注册 > 后注册
  3. 根据实现类型:BeanDefinitionRegistryPostProcessor > BeanFactoryPostProcessor
  4. 根据排序接口:PriorityOrdered > Ordered > 无排序(无排序时根据注入顺序)
  5. 根据排序序号:Integer.MIN_VALUE > Integer.MAX_VALUE

根据上面的优先级类型,我们排列出一个完整的优先级

出于美观考虑,简化一些写法

  • 把执行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 方法简化为:BDRPP#registry
  • 把执行BeanDefinitionRegistryPostProcessor#postProcessBeanFactory 方法简化为:BDRPP#beanfactory
  • 把执行BeanFactoryPostProcessor#postProcessBeanFactory 方法简化为: BFPP#beanfactory
  1. 手动注册 + BDRPP#registry
  2. 自动注册 + BDRPP#registry+ PriorityOrdered
  3. 自动注册 + BDRPP#registry+ Ordered
  4. 自动注册 + BDRPP#registry+ 无排序
  5. 手动注册 + BDRPP#beanfactory
  6. 手动注册 + BFPP#beanfactory
  7. 自动注册 + BDRPP#beanfactory(根据BDRPP#Registry的执行顺序)
  8. 自动注册 + BFPP#beanfactory
  9. 自动注册 + BFPP#beanfactory
  10. 自动注册 + BFPP#beanfactory+ 无排序

对于BDRPP#registry方法,如果高优先级注册了新的BDRPP定义,那么这个新的BDRPP会在次一级的优先级中执行。比如:在配置类中通过@Bean注入了新的BDRPP,由于配置类后处理器ConfigurationClassPostProcessor的优先级为PriorityOrdered,那么由配置类注入的新BDRPP(PriorityOrdered或Ordered)将在下一级优先级Ordered中执行,无排序类型同理。无排序优先级的内容将会循环执行,直到没有新的BDRPP定义产生。

这一节的内容绕是绕了点,不过如果需要自己实现BFPP的话,那么了解这些是非常有必要的,因为不同优先级的BFPP的作用范围不同,高优先级BFPP将会影响低优先级的表现,或者低优先级会覆盖高优先级的操作。所以如果你的BFPP不起作用的话可以先看看是否受到优先级影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1213013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

App Inventor 2 如何比较两个日期/时间?

利用计时器组件的相关方法: 比较两个日期/时间对象的毫秒数,具体请参见文档:https://www.fun123.cn/reference/ ... tml#Clock.GetMillis

Zookeeper Java SDK 开发入门

文章目录 一、概述二、导入依赖包三、与 Zookeeper 建立连接四、判断 ZooKeeper 节点是否存在四、创建 ZooKeeper 节点数据五、获取 ZooKeeper 节点数据六、修改 ZooKeeper 节点数据七、异步获取 ZooKeeper 节点数据八、完整示例 如果您还没有安装Zookeeper请看ZooKeeper 安装说…

竞赛选题 深度学习花卉识别 - python 机器视觉 opencv

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 &a…

消息通讯——MQTT WebHookSpringBoot案例

目录 EMQX WebHook介绍EMQX WebHook是什么EMQX WebHook配置项如何使用EMQX WebHook配置WebHook配置事件推送参数详解 SpringBoot集成Webhook实现客户端断连监控1. 实现前提2. 代码实现接口3. 监听结果 总结 EMQX WebHook介绍 EMQX WebHook是什么 EMQX WebHook 是由 emqx_web_…

Control的Invoke和BeginInvoke

近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下。感谢这篇文章对我的理解Invoke和BeginInvoke的真正含义 。 (一)Control的Invoke和BeginInvoke 我们要基于以下认识: (1&#x…

企业微信H5开发遇到的坑

企业微信官方推荐wx.agentConfig引用<script src"https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>是没有效果的 必须引用以下代码才有效果&#xff0c;这也是我看了社区的回答才有所收获&#xff0c;是一个坑 且VUE引用在线的…

C语言——分割单向链表

本文的内容是使用C语言分割单向链表&#xff0c;给出一个链表和一个值&#xff0c;要求链表中小于给定值的节点全都位于大于或等于给定值的节点之前&#xff0c;打印原始链表的所有元素和经此操作之后链表的所有元素。 分析&#xff1a;本题只是单向链表的分割&#xff0c;不涉…

【数据结构初阶】链表OJ

链表OJ 题目一&#xff1a;移除链表元素题目二&#xff1a;反转链表题目三&#xff1a;链表的中间节点题目四&#xff1a;链表中倒数第k个结点题目五&#xff1a;合并两个有序链表题目六&#xff1a;链表分割题目七&#xff1a;链表的回文结构题目八&#xff1a;相交链表题目九…

2023年【道路运输企业安全生产管理人员】证考试及道路运输企业安全生产管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年道路运输企业安全生产管理人员证考试为正在备考道路运输企业安全生产管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的道路运输企业安全生产管理人员模拟考试题祝您顺利通过道路运输企业安全生…

独家推荐:7个极品APP设计模板,助你塑造高级审美

用户的视觉体验在APP开发中也至关重要&#xff0c;市场上广受好评的APP离不开精致的APP界面设计。高质量的APP界面设计模板可以为高质量的APP界面设计做出贡献。本文结合即时设计资源社区10个高质量的APP界面设计模板进行分析介绍&#xff0c;让您的界面设计更加抢眼。看完这些…

用户画像与用户分层

用户画像是重要的数据产品和运营抓手&#xff0c;指能够描述和刻画用户信息和的数据指标。通过用户画像&#xff0c;业务经营团队可以充分、深入、准确地了解用户在不同生命周期的特征&#xff0c;来制定高效的用户经营策略。用户画像&#xff0c;不论 Persona 还是 Profile &a…

[已解决]使用order by 排序后的是10 6 7 8 9 而不是 6 7 8 9 10?

问题 sql order by 排序后的为什么 是10 6 7 8 9 而不是 6 7 8 9 10? 思路 在 SQL 中&#xff0c;ORDER BY 默认的排序方式是升序&#xff08;从小到大&#xff09;。所以&#xff0c;如果您简单地使用 ORDER BY 对某个列进行排序&#xff0c;它会将数字按照升序排列&#…

前端element的el-tooltip鼠标经过显示文字,没有文字显示空黑框问题

场景&#xff1a; 有时候在使用element的el-tooltip时会使用三元表达式&#xff0c;满足某个条件后才显示提示文字&#xff0c;否则不展示文字&#xff0c;但是却出现在在没有文字时展示了黑框&#xff0c;如下图&#xff1a; 解决方案&#xff1a; 加一个disabled便可&#…

colormap与colorbar应用

一&#xff0c;colormap 常见色度枚举值如下 应用如下 img cv2.applyColorMap(img, cv2.COLORMAP_JET) cv2.imshow(img,img) cv2.waitKey(0) cv2.destroyAllWindows() 常用的COLORMAP_JET效果如下&#xff0c;该模式常用于生成热力图 二&#xff0c;colorbar colorbar所有…

Python自动化测试:web自动化测试——Selenium框架

web自动化测试1 Selenium介绍web自动化实现原理环境准备1&#xff09;Seleniumpython环境搭建安装步骤环境变量的配置 2&#xff09;浏览器驱动驱动下载驱动环境配置 3&#xff09;版本检查4&#xff09;其他异常情况排查版本不一致未激活卸载、降低/升级setuptools版本 web自动…

day2324_jdbc

今日内容 零、 复习昨日 一、作业 二、SQL注入 三、PreparedStatement 四、事务 五、DBUtil 零、 复习昨日 一、引言 1.1 如何操作数据库 使用客户端工具访问数据库&#xff0c;需要手工建立连接&#xff0c;输入用户名和密码登录&#xff0c;编写 SQL 语句&#xff0c;点击执行…

沉浸式航天vr科普馆VR太空主题馆展示

科普教育从小做起&#xff0c;现在我们的很多地方小孩子游乐体验不单单只有草坪玩耍体验&#xff0c;还有很多科普知识的体验馆和游玩馆。虽然现在我们还不能真实的上太空或者潜入海底&#xff0c;但是这些现在已经可以逼真的展示在我们面前。通过一种虚拟现实技术手段。人们带…

深入解析JavaScript中的变量作用域与声明提升

JS中的变量作用域 背景&#xff1a; ​ 之前做js逆向的时候&#xff0c;有一个网站很有意思&#xff0c;就是先出现对其赋值&#xff0c;但是后来的变量赋值没有对其发生修改&#xff0c;决定说一下js中的作用域问题. 全局作用域&#xff1a; ​ 全局作用域的变量可以在任何…

QT使用Socket与安卓Socket互发消息

背景:安卓设备通过usb网络共享给Linux,此时安卓设备与linux处于同一网络环境,符合使用socket的条件,linux做客户端,安卓做服务端 1.QT使用Socket (1).在工程文件中加入 QT network (2).导包以及写一些槽函数用做数据传输与状态接收 #ifndef MAINWINDOW_H #define MAINWINDOW…