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

目录

  • 前言
  • 应用
    • 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 {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 注册BeanDefinition等相关内容RootBeanDefinition rbd = new RootBeanDefinition(ConfigBeanServiceImpl.class);registry.registerBeanDefinition("beanName", rbd);}@Overridepublic 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");}}@Overridepublic int getOrder() {// 执行的优先级序号return Ordered.LOWEST_PRECEDENCE;}
}

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

@Configuration
public class PostProcessorConfig {@Beanpublic 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.hqwc.cn/news/179503.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

轻量封装WebGPU渲染系统示例<32>- 若干线框对象(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/WireframeEntityTest.ts 当前示例运行效果: 此示例基于此渲染系统实现,当前示例TypeScript源码如下: export class WireframeEntityTest {private mRsc…

自学SLAM(8)《第四讲:相机模型与非线性优化》作业

前言 小编研究生的研究方向是视觉SLAM,目前在自学,本篇文章为初学高翔老师课的第四次作业。 文章目录 前言1.图像去畸变2.双目视差的使用3.矩阵微分4.高斯牛顿法的曲线拟合实验 1.图像去畸变 现实⽣活中的图像总存在畸变。原则上来说,针孔透…

(论文阅读24/100)Visual Tracking with Fully Convolutional Networks

文献阅读笔记(sel - CNN) 简介 题目 Visual Tracking with Fully Convolutional Networks 作者 Lijun Wang, Wanli Ouyang, Xiaogang Wang, and Huchuan Lu 原文链接 http://202.118.75.4/lu/Paper/ICCV2015/iccv15_lijun.pdf 【DeepLearning】…

java语言开发B/S架构医院云HIS系统源码【springboot】

医院云HIS全称为基于云计算的医疗卫生信息系统( Cloud- Based Healthcare Information System),是运用云计算、大数据、物联网等新兴信息技术,按照现代医疗卫生管理要求,在一定区域范围内以数字化形式提供医疗卫生行业数据收集、存储、传递、…

Linux常用的磁盘使用情况命令汇总

1、查看分区使用百分比 df -h 2、查看指定目录磁盘使用情况 du -hac --max-depth1 /opt 参数:-a 查看所有文件,-c 汇总统计,max-depth1 查看深度为1,2级目录不再统计。 3、常用统计命令汇总

[LeetCode]-622. 设计循环队列

目录 662. 设计循环队列 题目 思路 代码 662. 设计循环队列 622. 设计循环队列 - 力扣(LeetCode)https://leetcode.cn/problems/design-circular-queue/ 题目 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO&…

PostGIS学习教程六:几何图形(geometry)

文章目录 一、介绍二、元数据表三、表示真实世界的对象3.1、点(Points)3.2、线串(Linestring)3.3、多边形(Polygon)3.4、图形集合(Collection) 四、几何图形输入和输出五、从文本转换…

2023.11.16-hive sql高阶函数lateral view,与行转列,列转行

目录 0.lateral view简介 1.行转列 需求1: 需求2: 2.列转行 解题思路: 0.lateral view简介 hive函数 lateral view 主要功能是将原本汇总在一条(行)的数据拆分成多条(行)成虚拟表,再与原表进行笛卡尔积&#xff0c…

JDK5,7,11,17特性

目录 JDK5 基本数据类型自动装箱拆箱 可变参数 增强for 注解 泛型 枚举 概述 定义 常用方法 自定义构造方法 枚举类中的抽象方法 JDK7 二进制字面量 switch 异常 try-with-resources,自动关流 JDK11 FileInputStream增强 String类增强 Stream流…

LeetCode - 142. 环形链表 II (C语言,快慢指针,配图)

如果你对快慢指针,环形链表有疑问,可以参考下面这篇文章,了解什么是环形链表后,再做这道题会非常简单,也更容易理解下面的图片公式等。 LeetCode - 141. 环形链表 (C语言,快慢指针,…

制造企业需要哪些管理系统?怎么选才最划算?

制造企业需要哪些管理系统?怎么选才最划算? 一般来说,制造企业必须要有的几大业务模块及其对应的业务系统有: 生产计划和控制:生产计划系统、MRP(物料需求计划)系统、ERP(企业资源…

代码随想录图论部分-695. 岛屿的最大面积|1020. 飞地的数量

695. 岛屿的最大面积 题目:给你一个大小为 m x n 的二进制矩阵 grid 。岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水&#xff0…