Spring核心模块—— BeanFactoryPostProcessorBeanPostProcessor(后处理器)
后置处理器
- 前言
- Spring的后处理器
- BeanFactoryPostProcessor(工厂后处理器)
- 执行节点
- 作用
- 基本信息
- 经典场景
- 子接口——BeanDefinitiRegistryPostProcessor
- 基本介绍
- 用途
- 具体原理
- 例子——注册BeanDefinition
- 使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
- BeanPostPrecessor (后处理器)
- 基本原理
- 运用的场景
- 经典例子——时间日志增强
- BeanFactoryPostProcessor&&BeanPostProcessor的不同点
- 结尾
前言
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。
如果你不太清楚BeanDefinition是干嘛的,推进你看看我写的另外一篇文章,相信可以帮助到你:Spring核心模块解析—BeanDifinition。
Spring的后处理器
Sping的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,以及动态修改Bean的作用,那么Spring主要有两种后处理器,下面我们分别进行介绍他们的基本概念和业务场景。希望你可以更好的理解。
BeanFactoryPostProcessor(工厂后处理器)
执行节点
在BeanDefinitDefinitionMap填充完毕,Bean实例化之前执行。
作用
1.允许我们在Spring容器加载Bean定义之后,对Bean定义进行修改,从而影响到容器中实际实例化的Bean。
2.主要用途是应用程序上下文准备就绪前,修改或添加Bean定义。
3.也可以用来执行自定义的Bean验证或Bean初始化逻辑。
基本信息
beanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
@FunctionalInterface
public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;}
我们先来关注一下接口里面方法的参数:ConfigurableListableBeanFactory
本质来说,这个参数其实是BeanFacotry的子接口,我们看一下这层关系:
public interface ConfigurableListableBeanFactoryextends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory
然后进入到ListableBeanFactory
中:
public interface ListableBeanFactory extends BeanFactory
经典场景
业务场景:配置文件中的占位符替换为实际值的情况。比如将配置文件中的数据库连接URL,用户名和密码等信息替换为实际的值。
经典用途是用它实现属性占位符的解析,通过实现BeanFactoryPostProcessor
接口并重写postProcessBeanFactory()
,我们使用Spring的占位符解析机制(如${…})来解析Bean定义中的属性占位符,并将其替换为实际的属性值。
下面我们来介绍一下代码实现:
1.首先在Spring配置文件中定义PropertyPlaceholderConfigurer
实例,用来解析占位符。
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="location" value="classpath:jdbc.properties"/>
</bean>
上面的配置中,PropertyPlaceholderConfigurer
实例用于解析jdbc.properties
文件中的占位符。
2.实现一个BeanFactoryPostProcessor
接口的实现类,用于在应用程序上下文准备就绪之前替换Bean定义中的占位符,例如:
public class PropertyPlaceholderReplacer implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 获取名为 "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0" 的 PropertyPlaceholderConfigurer 对象// 该对象用于解析配置文件中的占位符,并将解析后的值设置回相应的 Bean 定义中PropertyPlaceholderConfigurer configurer = (PropertyPlaceholderConfigurer)beanFactory.getBean("org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0");// 获取所有的 Bean 名称String[] beanNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanNames) {// 获取 Bean 的定义BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);// 获取 Bean 的属性值MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();PropertyValue[] propertyValueArray = propertyValues.getPropertyValues();for (PropertyValue propertyValue : propertyValueArray) {Object value = propertyValue.getValue();// 如果属性值是一个字符串类型,那么就将其转换为实际的值if (value instanceof String) {// 调用 PropertyPlaceholderConfigurer 的 convertPropertyValue() 方法将占位符解析为实际的值Object convertedValue = configurer.convertPropertyValue((String) value);// 将转换后的值设置回属性值中propertyValue.setConvertedValue(convertedValue);}}}}
}
3.最后在Spring配置文件中注册PropertyPlaceholderReplacer
实例,让它参与应用程序上下文创建过程:
<bean class="com.example.PropertyPlaceholderReplacer"/>
Spring容器启动时,PropertyPlaceholderReplacer
实例会被创建并自动调用postProcessBeanFactory
方法,在此方法中实现占位符的解析,然后将解析后的实际值设置回相应的Bean
定义中。这样,应用程序中的占位符就会被正确地替换为实际的值。
子接口——BeanDefinitiRegistryPostProcessor
基本介绍
它是Spring框架的一个扩展点,用于对Bean定义的注册过程进行干预和定制。
继承BeanFactoryPostProcessor
接口,并在其基础上扩展了一个新的方法,即:postProcessBeanDefinitionRegistry()
方法。
用途
在Spring容器初始化时,首先会读取应用程序中的配置文件,并解析出所有的Bean定义,然后将这些Bean定义注册到容器中。
在这个过程中,BeanDefinitionRegistryProcessor
提供了一种机制,允许开发人员在Bean定义注册之前和之后对Bean定义进行自定义处理,例如添加,修改或删除Bean定义等。
具体原理
具体来说,BeanDefinitionRegistryPostProcessor
提供了以下两个方法:
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
:该方法在所有Bean定义加载完成之后,Bean实例化之前被调用,允许开发人员对Bean定义进行自定义修改,例如添加,修改或删除Bean定义等。postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
: 该方法是继承自BeanFactoryPostProcessor
接口的方法,用于在BeanFactory完成实例化之后对BeanFactory进行后置处理。
那么我们通过BeanDefinitionRegistryPostProcessor
接口,开发人员可以在Spring容器启动时干预Bean的注册过程,从而实现对Bean的自定义处理。
例如:
可以通过该接口来动态注册Bean定义,从而实现基于注解或者其他方式的自动发现和注册Bean。
同时,该接口也提供了一种扩展点,允许开发人员在Bean定义注册之后再进行后置处理,例如对Bean的属性进行统一设置,验证等操作。
例子——注册BeanDefinition
BeanDefinitionRegistryPostProcessor
接口主要用于注册和修改BeanDefinition
。
在postProcessBeanDefinitionRegistry()
方法中,开发人员可以通过BeanDefinitionRegistry
对象注册新的BeanDefinition
,例如:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;/*** 自定义的BeanDefinitionRegistryPostProcessor*/
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {/*** 重写BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法*/@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 创建一个RootBeanDefinition实例,该实例对应的BeanClass是MyBeanRootBeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);// 向BeanDefinitionRegistry注册MyBean的BeanDefinitionregistry.registerBeanDefinition("myBean", beanDefinition);}/*** 重写BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法*/@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// do nothing}/*** 自定义的Bean*/public static class MyBean {private String message = "Hello, World!";public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
}
在应用程序的Spring配置文件中,我们可以通过下面的方式来注册MyBeanDefinitionRegistryPostProcessor
<bean class="com.example.MyBeanDefinitionRegistryPostProcessor"/>
在容器启动时,Spring会自动检测到MyBeanDefinitionRegistryPostProcessor
并将其实例化和注册到容器中。在容器启动完成后,我们的myBean
就可以在容器中被其他的Bean所依赖和使用了。
后面针对这个执行顺序,我们引用一个图来说明,相信看到这你就明白啦:
你以为到这就结束啦吗?咱们No,再来一个Demo收尾!
使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
——todo晚上回宿舍补上
BeanPostPrecessor (后处理器)
Bean实例化之后,到最终缓存到名为singletonObjects的单例池之前,中间会经过Bean的初始化过程。例如:属性的填充,初始化init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,它允许在Bean的初始化流程中插入自定义的逻辑。我们称之为BeanPostProcessor,会在流程节点上被Spring自动调用。
基本原理
它定义了两个方法:
postProcessBeforeInitialization(Object bean, String beanName)
:在 Bean 初始化之前调用。
postProcessAfterInitialization(Object bean, String beanName)
:在 Bean 初始化之后调用。
我们可以通过实现BeanPostProcessor
接口,并将其注册到Spring容器中,来实现对Bean初始化过程的控制和定制。
在这两个方法中,我们可以根据需要对Bean进行一些自定义操作,例如:修改属性值,注入依赖,动态代理等等。
在 Spring 容器启动的过程:
1.当一个 Bean 被实例化后,Spring 容器会按照一定的顺序调用注册在容器中的所有 BeanPostProcessor
实现的postProcessBeforeInitialization
方法,
2.然后调用 Bean 的 init 方法,最后再按照相反的顺序调用所有 BeanPostProcessor
实现的 postProcessAfterInitialization
方法。
这个过程中,BeanPostProcessor
扮演着一个监听器的角色,允许开发人员在 Bean 初始化的过程中干预和修改 Bean 的行为,从而实现对 Spring 容器的定制和扩展。
运用的场景
-
日志记录:
可以使用BeanPostProcessor来实现在Bean实例化后、初始化前后打印日志,从而实现记录Bean生命周期的目的。 -
数据库连接管理:
可以使用BeanPostProcessor来在Bean实例化后、初始化前后获取并设置数据库连接,从而实现数据库连接的管理和复用。 -
安全检查:
可以使用BeanPostProcessor来实现在Bean实例化后、初始化前后对Bean的属性进行安全检查,例如检查是否有不安全的属性或敏感的数据,从而保证系统的安全性。 -
AOP(面向切面编程):
可以使用BeanPostProcessor来在Bean实例化后、初始化前后进行动态代理,从而实现AOP编程,例如实现事务控制、日志记录等功能。 -
自定义注解:
可以使用BeanPostProcessor来实现在Bean实例化后、初始化前后扫描并解析自定义注解,从而实现自定义注解的功能。例如,可以在Bean实例化后、初始化前后,扫描并解析所有的@MyAnnotation注解,并根据注解的内容进行相应的操作。
经典例子——时间日志增强
要求:
- Bean的方法执行之前控制台打印当前时间。
- Bean的方法执行之后控制台打印当前时间。
分析: - 对方法进行增强主要是代理设计模式和包装设计模式
- 由于Bean方法不确定,所以使用动态代理在运行期间执行增强操作
- 在Bean实例创建完毕后,进入到单例池之前,使用Proxy代替真正的目标Bean。
——todo晚上回宿舍补上
最后梳理一下,如果你前面耐心看完,这张图不用多解释了,一看就明白了🫡:
BeanFactoryPostProcessor&&BeanPostProcessor的不同点
BeanFactoryPostProcessor
是Spring容器中用于在Bean定义加载到容器后、实例化之前对Bean的定义进行修改和扩展的扩展点。
它可以通过实现BeanFactoryPostProcessor
接口来注册自己的处理逻辑,对所有的Bean定义进行修改和扩展,包括修改Bean的属性值、修改Bean的依赖关系、注册新的Bean定义等等。
通常情况下,我们可以使用BeanFactoryPostProcessor
来实现一些与Bean定义相关的操作,例如通过解析外部配置文件动态修改Bean的属性值、实现Bean依赖的自动装配、注册新的Bean定义等等。
BeanPostProcessor
是Spring容器中用于在Bean实例化后、初始化前后对Bean进行自定义处理的扩展点。
它可以通过实现BeanPostProcessor
接口来注册自己的处理逻辑,对所有的Bean实例进行修改和扩展,包括修改Bean的属性值、动态代理Bean、实现AOP编程、记录日志等等。
通常情况下,我们可以使用BeanPostProcessor
来实现一些与Bean实例相关的操作,例如实现Bean的自定义初始化逻辑、记录Bean的生命周期、实现Bean的动态代理等等。
结尾
这块知识,无论是学Bean的生命周期还是刷新逻辑都很重要,希望你可以学会,如果有哪些地方觉得总结的不好,欢迎留言。🙏
相关文章:

Spring核心模块—— BeanFactoryPostProcessorBeanPostProcessor(后处理器)
后置处理器前言Spring的后处理器BeanFactoryPostProcessor(工厂后处理器)执行节点作用基本信息经典场景子接口——BeanDefinitiRegistryPostProcessor基本介绍用途具体原理例子——注册BeanDefinition使用Spring的BeanFactoryPostProcessor扩展点完成自定…...

产品新人如何培养产品思维?
什么是产品思维?其实很难定义,不同人有不同的定义。有的人定义为以用户为中心打磨一个完美体验的产品;有的定义为从需求调研到需求上线各个步骤需要思考的点,等等。本文想讨论的产品思维是:怎么去发现问题,…...

「兔了个兔」CSS如此之美,看我如何实现可爱兔兔LOADING页面(万字详解附源码)
💂作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后…...

【Java】阻塞队列 BlcokingQueue 原理、与等待唤醒机制condition/await/singal的关系、多线程安全总结
在实习过程中使用阻塞队列对while sleep 轮询机制进行了改造,提升了发送接收的效率,这里做一点点总结。 自从Java 1.5之后,在java.util.concurrent包下提供了若干个阻塞队列,BlcokingQueue继承了Queue接口,是线程安全…...

【水下图像增强】Enhancing Underwater Imagery using Generative Adversarial Networks
原始题目Enhancing Underwater Imagery using Generative Adversarial Networks中文名称使用 GAN 增强水下图像发表时间2018年1月11日平台ICRA 2018来源University of Minnesota, Minneapolis MN文章链接https://arxiv.org/abs/1801.04011开源代码官方:https://gith…...
Maven专题总结—详细版
第一章 为什么使用Maven 获取jar包 使用Maven之前,自行在网络中下载jar包,效率较低。如【谷歌、百度、CSDN…】使用Maven之后,统一在一个地址下载资源jar包【阿里云镜像服务器等…】 添加jar包 使用Maven之前,将jar复制到项目工程…...
华为OD机试真题Java实现【字符串加密】真题+解题思路+代码(20222023)
字符串加密 题目 给你一串未加密的字符串str, 通过对字符串的每一个字母进行改变来实现加密, 加密方式是在每一个字母str[i]偏移特定数组元素a[i]的量, 数组a前三位已经赋值:a[0]=1,a[1]=2,a[2]=4。 当i>=3时,数组元素a[i]=a[i-1]+a[i-2]+a[i-3], 例如:原文 abcde …...
「Python 基础」函数与高阶函数
文章目录1. 函数调用函数定义函数函数的参数递归函数2. 高阶函数map/reducefiltersorted3. 函数式编程返回函数匿名函数装饰器偏函数1. 函数 函数是一种重复代码的抽象方式,Python 内建支持的一种封装; 调用函数 调用一个函数,需要知道函数…...
DIV内容滚动,文字符滚动标签marquee兼容稳定不卡
marquee(文字滚动)标签 marquee简介 <marquee>标签,是成对出现的标签,首标签<marquee>和尾标签</marquee>之间的内容就是滚动内容。 <marquee>标签的属性主要有behavior、bgcolor、direction、width、height、hspace、vspace、loop、scrollamount、scr…...

SpringBoot_第五章(Web和原理分析)
目录 1:静态资源 1.1:静态资源访问 1.2:静态资源源码解析-到WebMvcAutoConfiguration 2:Rest请求绑定(设置put和delete) 2.1:代码实例 2.2:源码分析到-WebMvcAutoConfiguratio…...

4-2 Linux进程和内存概念
文章目录前言进程状态进程优先级内存模型进程内存关系前言 进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源。一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以在任何时刻内存中都只有代码的一份拷贝。 进程状态…...

【微信小程序】计算器案例
🏆今日学习目标:第二十一期——计算器案例 ✨个人主页:颜颜yan_的个人主页 ⏰预计时间:30分钟 🎉专栏系列:我的第一个微信小程序 计算器前言实现效果实现步骤wxmlwxssjs数字按钮事件处理函数计算按钮处理事…...

408 计算机基础复试笔记 —— 更新中
计算机组成原理 计算机系统概述 问题一、冯诺依曼机基本思想 存储程序:程序和数据都存储在同一个内存中,计算机可以根据指令集执行存储在内存中的程序。这使得程序具有高度灵活性和可重用性。指令流水线:将指令分成若干阶段,每…...
找出最大数-课后程序(Python程序开发案例教程-黑马程序员编著-第二章-课后作业)
实例6:找出最大数 “脑力大乱斗”休闲益智游戏的关卡中,有一个题目是找出最大数。本实例要求编写程序,实现从输入的任意三个数中找出最大数的功能。 实例分析 对于3个数比较大小,我们可以首先先对两个数的大小进行比较ÿ…...

Java——N叉树的层序遍历
题目链接 leetcode在线oj题——N叉树的层序遍历 题目描述 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例&…...
【Kubernetes】第十八篇 - k8s 服务发现简介
一,前言 上一篇,介绍了阿里云 ECS 服务器重启后的环境修复; 本篇,介绍 k8s 服务发现; 二,服务发现简介 当 A服务依赖了 B服务,而 B服务的IP和端口未知(或相对不固定)&…...
Codeforces Round 856 (Div. 2) 最好ak的div2
最近几场的div2 E都是一个思路啊,代码大差不差的,感觉随便ak啊。 A. Prefix and Suffix Array 题意 给你前n−1n-1n−1个字符串前缀和后n−1n-1n−1个字符串后缀,判断原字符串是否是回文串 思路 相同长度的判断是否是对称的即可。 代码 B C…...
最新JVM技术: GraalVM,让你一文了解它的方方面面
1. 什么是GraalVM? GraalVM是一种开源的虚拟机平台,由Oracle公司开发。它支持多种编程语言,包括Java、JavaScript、Python、Ruby、R、C++等,旨在提高应用程序的性能和扩展性。 GraalVM通过提供即时编译器(Just-in-Time Compiler,JIT)和Ahead-of-Time(AOT)编译器来提…...
MySQL索引失效的场景
1.like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。 2.数据库表数据量过小 如果表的数据量非常小,则MySQL可能不会使用索引,因为它认为全表扫描的代价更小。 3.or语句前后没有同时使用索引 …...

Java - 对象的比较
一、问题提出 前面讲了优先级队列,优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,为了简单起见,我们只是插入了Integer类型, 那优先级队列中能否插入自定义类型对象呢…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...