Spring如何进行动态注册Bean

在Spring框架中,Bean是应用程序的核心组成部分,而BeanDefinition则是这些Bean的元数据表示。随着应用程序的复杂性增加,我们可能需要更灵活地定义和注册Bean。Spring框架提供了几个扩展点,允许我们以编程方式影响Bean的创建和定义过程。本文将深入探讨BeanDefinitionRegistryPostProcessor、ImportBeanDefinitionRegistrar和BeanFactoryPostProcessor这三个重要的扩展点。

1.BeanFactoryPostProcessor

BeanFactoryPostProcessor是一个重要的扩展点,它允许你在Spring容器实例化bean之前修改bean的定义。BeanFactoryPostProcessor接口定义了一个postProcessBeanFactory方法,该方法在Spring IoC容器实例化所有的bean定义之后,但在实例化任何bean之前被调用。

public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

示例:

修改BeanDefinition

public class BeanFactoryTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.scan("org.example.beanFactory");context.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());context.refresh();System.out.println( context.getBean("testBean1"));}
}public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("MyBeanFactoryPostProcessor->postProcessBeanFactory");DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestBean2.class);defaultListableBeanFactory.registerBeanDefinition("testBean1",beanDefinitionBuilder.getBeanDefinition());}
}@Component
public class TestBean1 {
}public class TestBean2 {
}

上面的代码会进行修改TestBean1的BeanDefinition,如果我们将 context.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());这行代码注释掉之后,这块代码的执行结果为:

我们把注释放开之后,执行的结果就会变成

这个是因为我们在MyBeanFactoryPostProcessor中进行修改了testBean1的BeanDefinition所以就会导致这个结论。

注册新的BeanDefinition

对上面的代码我们可以进行改造一下

public class BeanFactoryTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.scan("org.example.beanFactory");context.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());context.refresh();System.out.println( context.getBean("testBean1"));System.out.println( context.getBean("testBean2"));}
}public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("MyBeanFactoryPostProcessor->postProcessBeanFactory");DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestBean2.class);defaultListableBeanFactory.registerBeanDefinition("testBean2",beanDefinitionBuilder.getBeanDefinition());}
}

执行结果为:

上面的代码我们可以看到TestBean2这个类,我们并没有通过注解交给spring管理,但我们还是可以从spring容器中进行获取到TestBean2的对象信息。这是因为我们在MyBeanFactoryPostProcessor进行添加了TestBean2的BeanDefinition。

2.BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

相比于BeanFactoryPostProcessor多了一个postProcessBeanDefinitionRegistry方法。这个方法是是针对BeanDefinition进行一些修改的操作。那这块就有一个疑问了为何有了BeanFactoryPostProcessor还需要BeanDefinitionRegistryPostProcessor呢?这是为什么呢,我觉得最主要得原因是执行时机的不同。BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor这两个类执行的时机不一样.sprin在进行执行的时候,会先进行执行BeanDefinitionRegistryPostProcessor实现类的中的postProcessBeanDefinitionRegistry方法,然后在进行执行BeanDefinitionRegistryPostProcessor实现类的postProcessBeanFactory方法,最后再进行执行BeanFactoryPostProcessor的postProcessBeanFactory方法。

示例:

//实现BeanFactoryPostProcessor接口
@Component
public class TestParent implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("TestParent->postProcessBeanFactory");}
}//实现BeanDefinitionRegistryPostProcessor接口
@Component
public class TestSub implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println("TestSub->postProcessBeanDefinitionRegistry");}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("TestSub->postProcessBeanFactory");}
}

代码执行结果:

可以针对BeanDefinitionRegistryPostProcessor优先于BeanFactoryPostProcessor的执行特性,做一些特定化的操作。

3.ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar接口中有一个核心方法是registerBeanDefinitions方法。

BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor这两个接口都可以进行修改BeanDefinition,也可以进行添加BeanDefinition的定义,但spring不是很推荐使用BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor来进行BeanDefinition的新增,修改等操作,是比较推荐使用ImportBeanDefinitionRegistrar来进行BeanDefinition的添加,修改等操作。这是为啥呢。因为BeanDefinitionRegistryPostProcessor可能会创建出来一个半成品的bean来,我们来举例说明。

刚开始的时候我们进行创建两个类TestBean和TestBean1,在TestBean类中进行使用TestBean1这个类并用@Bean注解进行声明。

/此处的TestBean是没有用@Compent注解进行修饰的
public class TestBean {public TestBean(){System.out.println("testBean");}@Beanpublic TestBean1 getTestBean1(){return new TestBean1();}}public class TestBean1 {public TestBean1(){System.out.println("testBean1");}
}

我们使用BeanDefinitionRegistryPostProcessor来进行添加TestBean的BeanDefinition。

@Component
public class TestSub implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println("TestSub->postProcessBeanDefinitionRegistry");BeanDefinitionBuilder testBeanDefinition= BeanDefinitionBuilder.genericBeanDefinition(TestBean.class);registry.registerBeanDefinition("testBean",testBeanDefinition.getBeanDefinition());}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("TestSub->postProcessBeanFactory");}}
public static void main(String[] args) {AnnotationConfigApplicationContextcontext = new AnnotationConfigApplicationContext();context.scan("com.spring.example.beanDefinition");context.refresh();}

这块我们进行执行的时候发现 只进行执行了TestBean的构造方法中逻辑,TestBean1的构造方法并没有进行执行。那如果我们需要进行TestBean1的构造方法的时候,那么就需要进行实现ImportBeanDefinitionRegistrar接口。并且使用@Import注解,下面是使用ImportBeanDefinitionRegistrar接口的示例:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {BeanDefinitionBuilder testBean= BeanDefinitionBuilder.genericBeanDefinition(TestBean.class);registry.registerBeanDefinition("testBean",testBean.getBeanDefinition());}
}@Import(MyImportBeanDefinitionRegistrar.class)
public class Test {
}

4.总结

BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor,ImportBeanDefinitionRegistrar这三个类都可以进行BeanDefinition的添加,修改等操作。

BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor这两个类可以看成一个类,因为BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的一个子类。BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor这两个类的主要区别在于这两个类的执行时机不同,spring在执行的时候会首先进行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,其次进行执行BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法,最后再进行BeanFactoryPostProcessor的postProcessBeanFactory方法。

BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor在进行BeanDefinition的添加的时候,如果要进行添加BeanDefinition中有一些比较特殊的方法(例@Bean 注解),可能会导致一些bean的创建,会创建一些半成品的bean。想要创建成一个完整的Bean,还是得使用ImportBeanDefinitionRegistrar接口来进行操作BeanDefinition。

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

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

相关文章

【vulhub】FRISTILEAKS:1.3

目录 下载地址 1、信息收集获取ip获取端口目录扫描 2、漏洞利用3、提权反弹shell脚本检测脏牛提权 下载地址 FristiLeaks: 1.3 ~ VulnHub 1、信息收集 获取ip 打开靶机就可以看到Ip 192.168.8.23 获取端口 fscan扫一下 获取80端口 目录扫描 网站访问 192.168.8.23:80…

内行人才知道的白酒术语

😜宝子们,今天来给大家分享一些只有内行人懂的白酒术语,让你在酒桌上也能显得很专业!💪 ⬆️基酒术语解释:所谓基酒就是最基础的酒,也叫原浆酒,是指成酒后不经过勾调的酒液。基酒度…

烟雾监测与太阳能源:实验装置在其中的作用

太阳光在烟雾中的散射效应研究实验装置是一款模拟阳光透过烟雾环境的设备。此装置能帮助探究阳光在烟雾中的传播特性、散射特性及其对阳光的影响。 该装置主要包括光源单元、烟雾发生装置、光学组件、以及系统。光源单元负责产生类似于太阳光的光线,通常选用高亮度的…

迈克尔的44岁:时间的感悟与人生的智慧

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Ubuntu Desktop Docker 配置代理

Ubuntu Desktop Docker 配置代理 主要解决 docker pull 拉取不了镜像问题. Docker Desktop 配置代理 这个比较简单, 直接在 Docker Desktop 里设置 Proxies, 示例如下: http://127.0.0.1:7890 Docker Engine 配置代理 1.Docker Engine 使用下面配置文件即可, root 用户可…

动手学深度学习6.3 填充和步幅-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。 本节课程地址:填充和步幅_哔哩哔哩_bilibili 代码实现_哔哩哔哩_bilibili 本节教材地址:6.3. 填充和…

Linux下Qt程序打包

文章目录 一、前言二、linuxdeployqt下载安装三、Qt环境变量配置四、准备Qt可执行文件五、打包六、封装成deb安装包 一、前言 在Windows下进行Qt开发,软件开发好之后可以使用windeployqt进行打包,然后程序就可以移动到其它电脑上运行了 在Linux下同样可…

浅析stm32启动文件

浅析stm32启动文件 文章目录 浅析stm32启动文件1.什么是启动文件?2.启动文件的命名规则3.stm32芯片的命名规则 1.什么是启动文件? 我们来看gpt给出的答案: STM32的启动文件是一个关键的汇编语言源文件,它负责在微控制器上电或复位…

开箱即用的AI!九州未来亓绚AI教培一体机全新发布

以大模型、生成式人工智能为代表的人工智能技术在全球引起广泛关注,亦成为催生教育变革的重要力量。 中小学人工智能教育逐步推进,但实施过程中仍然面对诸多挑战。如何更广泛、高质量地开展中小学人工智能教育,成为当下我国教育改革创新的重…

CentOS7 虚谷数据库 单机版部署

单机版最低配置: 安装环境配置 1.CPU设置 关闭 CPU 超线程 查看当前CPU超线程状态: cat /sys/devices/system/cpu/smt/active 如果是0,表示超线程已关闭;返回值是1,表示超线程已开启。 切换超线程状态: &a…

景区客流统计系统提升服务精准度

在当今旅游业蓬勃发展的时代,景区面临着越来越多的挑战和机遇。如何在保障游客良好体验的同时,实现景区的高效管理和可持续发展,成为了摆在景区管理者面前的重要课题。景区客流统计系统的出现,为解决这一问题提供了有力的支持&…

vscode 打开远程bug vscode Failed to parse remote port from server output

vscode 打开远程bug vscode Failed to parse remote port from server output 原因如图: 解决:

Redis实战—附近商铺、用户签到、UV统计

本博客为个人学习笔记,学习网站与详细见:黑马程序员Redis入门到实战 P88 - P95 目录 附近商铺 数据导入 功能实现 用户签到 签到功能 连续签到统计 UV统计 附近商铺 利用Redis中的GEO数据结构实现附近商铺功能,常见命令如下图所示。…

Monsters Pack 04(游戏卡通可爱怪兽怪物战士模型)

以下模型有3种进化形态: 捕手战士 鱼卫战士 骑士战士 小鬼战士 猴东战士 无鼻战士 坑娃战士 刺头战士 树斯特战士 楔形战士 这些模型是为您的主要角色设计的敌人。进化的每个阶段都会使他变得更加强大,因此您可以用它来增强对手的实力,并作为敌人的boss。 它适用于不同类型的…

算法实验3:贪心算法的应用

实验内容 &#xff08;1&#xff09;活动安排问题 设有n个活动的集合E{1, 2, …, n}&#xff0c;其中每个活动都要求使用同一资源&#xff0c;而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi&#xff0c;且si <f…

厂家置换电费如何达到最大化收益

新能源行业知识体系-------主目录-----持续更新https://blog.csdn.net/grd_java/article/details/140004020 文章目录 一、电能电费二、同时刻不同厂家置换&#xff0c;不会影响最终电能电费结果三、风险防范补偿和回收机制四、我们的数据如何考虑补偿和回收五、如何利用补偿和…

java.lang.IllegalArgumentException: Illegal character in path at index 40解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

RFID涉密载体管控系统|DW-S402功能介绍

文件载体管控系统DW-S402是用于对各种载体进行有效管理的智能柜&#xff08;智能管理系统&#xff09;&#xff0c;实现对载体的智能化、规范化、标准化管理&#xff0c;广泛应用于保密、机要单位以及企事业单位等有载体保管需求的行业。 区域监控管理 主要是通过在需要监控的…

Mysql缓存调优的基本知识(附Demo)

目录 前言1. 配置2. 缓存3. 策略 前言 基本的知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;Mysql优化高级篇&#xff08;全&#xff09;Mysql底层原理详细剖析常见面试题&#xff08;全&#xff09; MySQL…

视图库对接系列(GA-T 1400)十九、视图库对接系列(级联)注册

背景 在上一章视图库对接系列(GA-T 1400)十八、视图库对接系列(级联)代码生成中我们已经把代码生成了,那怎么实现级联? 我们可以抓包看设备是怎么注册到我们平台的, 那我们就怎么实现就可以了。 实现 先看设备注册到我们服务端的包 步骤 注册我们可以参考视图库对接系列(…