【小白的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.mzph.cn/news/145834.shtml

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

相关文章

信息的浏览

万维网(WWW)是目前Internet上最流行的一种服务,它是建立在Internet上的多媒体集合信息系统。它利用超媒体的信息获取技术,通过一种超文本的表达方式,将所有WWW上的信息连接在一起。我们使用浏览器浏览网上的信息。 ▶浏览器 浏览器是指可以…

PHP低版本安全问题

目录 1、PHP弱类型问题 1.1 MD5、 SHA1 弱比较问题 1.2 数组 0 1)函数无法处理数组,返回0 2)strcmp 2、特殊字符串导致的问题 2.1 "ffifdyop" 与 md5(string,raw) 2.2 ereg函数漏洞:00 截断 3、正则匹配问…

物联网AI MicroPython学习之语法UART通用异步通信

学物联网,来万物简单IoT物联网!! UART 介绍 模块功能: UART通过串行异步收发通信 接口说明 UART - 构建UART对象 函数原型:UART(id, baudrate,bits, parity,stop, tx, rx)参数说明: 参数类…

3GPP协议解读(一)_23.501_23.502_PDU Session_SMF与UDP的交互

UE发起计算服务申请后,网络侧处理的流程 UE发起服务的流程:service request网络侧处理服务涉及的通信数据通过PDU Session进行传输,涉及到SMF与UPF的交互。PDU Session的建立、管理全部由SMF(Session Management Function&#x…

Git通过rebase合并多个commit

在使用 Git 作为版本控制的时候,我们可能会由于各种各样的原因提交了许多临时的 commit,而这些 commit 拼接起来才是完整的任务。那么我们为了避免太多的 commit 而造成版本控制的混乱,通常我们推荐将这些 commit 合并成一个。 1. 查看提交历…

基于JavaWeb+SpringBoot+掌上社区疫苗微信小程序系统的设计和实现

基于JavaWebSpringBoot掌上社区疫苗微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种…

2. zk集群部署

简介 上一篇文章我们已经把环境准备好了,jdk也配置好了,下面我们开始把zk部署起来 hadoop环境准备 创建zk用户 useradd zk -d /home/zk echo "1q1w1e1r" | passwd --stdin zk上传zk包 拷贝zk包到/home/zk目录,这里的zk版本为 3.6.3 scp…

windows使用lcx端口转发登陆远程主机

1.编译lcx源码: GitHub - UndefinedIdentifier/LCX: 自修改免杀lcx端口转发工具 2.在win7上安装vs2010并编译生成lcx.exe 3.在要被控制主机上运行: lcx -slave 192.168.31.248 51 192.168.31.211 3389 192.168.31.248为远程主控制主机,51为远程主机端口 192.168.31.211为被…

Java笔试题

5.18:JAVA面试题 Java笔试题目及答案

C语言--写一个函数返回bool值,来判断给定的字符串A和B(假设都是小写字母),是否是B中的字符都存在于A中,如果是返回true,否则返回false

一.题目描述 写一个函数返回bool值,来判断给定的字符串A和B(假设都是小写字母),是否是B中的字符都存在于A中,如果是返回true,否则返回false。例如: 字符串A:abcde 字符串B&#xff…

顶部动态菜单栏的使用

效果图 开发环境 vue3 关键逻辑 //导航栏状态选择 const navbarSolid ref(false); //初始化导航栏高度 const navbarHeight ref(0);/*** 根据滚动距离改变样式*/ function checkNavbarOpacity() {navbarSolid.value window.pageYOffset > navbarHeight.value / 2; }/**…

iOS UITableView获取到的contentSize不正确

在开发中遇到一个需求,就是将一个tableView的contentsize设置成该 tableView的frame的size,但是 经过调试,发现获取到的contentsize不争确,后来发现是 没有设置一个属性 if (available(iOS 15.0, *)) {_tableView.sectionHeaderTopPadding …

DDOS和CC攻击区别,哪种对服务器伤害大

ddos攻击主要是针对IP,针对IP进行发送大量报文进行攻击,导致服务器过载,一个IP的正常流量是有限的,如果被长期占用带宽过大那么就会直接导致服务器直接宕机,那么正常用户干脆直接访问不了服务器,也没有办法…

html使用天地图写一个地图列表

一、效果图&#xff1a; 点击左侧地址列表&#xff0c;右侧地图跟着改变。 二、代码实现&#xff1a; 一进入页面时&#xff0c;通过body调用onLoad"onLoad()"函数&#xff0c;确保地图正常显示。 <body onLoad"onLoad()"><!--左侧代码-->…

malloc

​​​​​​​在Linux上怎么实现C语言的malloc函数_哔哩哔哩_bilibili

py Selenium来启动多个浏览器窗口或标签页,并操作它们

使用Selenium库可以启动多个浏览器窗口或标签页&#xff0c;并进行操作。以下是一个简单的示例代码&#xff0c;演示如何使用Selenium启动多个浏览器窗口并操作它们&#xff1a; from selenium import webdriver# 创建多个浏览器窗口 driver_list [] for i in range(5):drive…

Python入门简介及下载安装,超详细教学!

文章目录 一、Python简介&#xff1a;Python解释器的类型Python的运行机制1、查看 Python 版本2、第一个Python3.x程序3、Python 应用 二、Python安装&#xff08;windows&#xff09;1、下载2、安装步骤&#xff1a; 三、运行Python1、交互式解释器&#xff1a;扩展&#xff1…

鸿蒙LiteOs读源码教程+向LiteOS中添加一个系统调用

本文分为2个部分&#xff1a;第1部分简要介绍如何读鸿蒙Liteos源码&#xff0c;第2部分是实验向LiteOS中添加一个系统调用的完整过程。 前置资料&#xff1a; imx6ull开发板使用方式详解 源码下载 编译运行简单程序 Ubuntu虚拟机使用鸿蒙LiteOs操作系统常见错误汇总 一、鸿…

在Qt设计师(Qt Designer )控件面板加入自定义控件

目录 1. 问题的提出 2. 本次开发环境说明 3. 具体实现 4. 注意的问题 5. 参考链接 1. 问题的提出 在Qt开发中&#xff0c;经常利用Qt设计师&#xff08;Qt Designer &#xff09;把界面设计好&#xff0c;将界面放到ui文件中&#xff0c;将逻辑处理放到cpp文件中&#xff…

计算机视觉基础(9)——相机标定与对极几何

前言 本节我们将学习相机标定和对极几何两部分的内容。 在相机标定部分&#xff0c;我们将学习直接线性变换&#xff08;Direct Linear Transform, DL&#xff09;,张正友标定法&#xff08;Zhang’s Method&#xff09;和 Perspective-n-Point (PnP) 这三种方法。 在对极几何部…