Spring如何进行动态注册Bean

news2024/11/14 21:57:43

在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 {
    @Override
    public 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 {
    @Override
    public 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 {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("TestParent->postProcessBeanFactory");

	}
}

//实现BeanDefinitionRegistryPostProcessor接口
@Component
public class TestSub implements BeanDefinitionRegistryPostProcessor {

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		System.out.println("TestSub->postProcessBeanDefinitionRegistry");
	}

	@Override
	public 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");
	}

	@Bean
	public TestBean1 getTestBean1(){
		return new TestBean1();
	}

}

public class TestBean1 {
  public TestBean1(){
    System.out.println("testBean1");
   }
}

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

@Component
public class TestSub implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		System.out.println("TestSub->postProcessBeanDefinitionRegistry");
		BeanDefinitionBuilder testBeanDefinition= BeanDefinitionBuilder.genericBeanDefinition(TestBean.class);
		registry.registerBeanDefinition("testBean",testBeanDefinition.getBeanDefinition());
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("TestSub->postProcessBeanFactory");

	}


}

public static void main(String[] args) {

		AnnotationConfigApplicationContext
				context = new AnnotationConfigApplicationContext();
		context.scan("com.spring.example.beanDefinition");
		context.refresh();
	}

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

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public 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://www.coloradmin.cn/o/1931993.html

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

相关文章

Window中 Redis下载安装

Redis7.2.3连接: 我用夸克网盘分享了「redis-windows-7.2.3.zip」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。 链接:https://pan.quark.cn/s/4dfb0497707a 在安…

Uniapp基础篇(持续更新)

1. Uni-app常用内置组件 view 视图容器 scroll-view 可滚动视图区域,用于区域滚动。需注意在webview渲染的页面中,区域滚动的性能不及页面滚动。 swiper 滑块视图容器。一般用于左右滑动或上下滑动,比如banner轮播图。 image uniapp官方iam…

封装网络请求 鸿蒙APP HarmonyOS ArkTS

一、效果展示 通过在页面直接调用 userLogin(params) 方法,获取登录令牌 二、申请网络权限 访问网络时候首先需要申请网络权限,需要修改 src/main 目录下的 module.json5 文件,加入 requestPermissions 属性,详见官方文档 【声明权…

Flink底层原理解析:案例解析(第37天)

系列文章目录 一、flink架构 二、Flink底层原理解析 三、Flink应用场景解析 四、fink入门案例解析 文章目录 系列文章目录前言一、flink架构1. 作业管理器(JobManager)2. 资源管理器(ResourceManager)3. 任务管理器(Ta…

Hadoop安装报错

报错:ERROR 2023-03-09 21:33:00,178 NetUtil.py:97 - SSLError: Failed to connect. Please check openssl library versions. 解决方案: 在安装失败得客户端执行 编辑 /etc/python/cert-verification.cfg 配置文件,将 [https] 节的 verify 项 设为禁用…

【教学类-67-02】20240716毛毛虫ABB排序

背景需求: 【教学类-67-01】20240715毛毛虫AB排序-CSDN博客文章浏览阅读584次,点赞16次,收藏6次。【教学类-67-01】20240715毛毛虫AB排序https://blog.csdn.net/reasonsummer/article/details/140443310 在AB排序基础上,继续制作…

【JavaEE精炼宝库】 初识网络原理——网络通信基础 | 协议

文章目录 一、网络发展史1.1 独立模式:1.2 网络互连:1.3 局域网(LAN):1.4 广域网(WAN): 二、网络通信基础2.1 IP地址:2.2 端口号: 三、协议3.1 协议的概念&am…

RabbitMQ:基础篇

1.RabbitMQ是高性能的异步通讯组件 何为异步通讯 打电话就是同步通讯,微信聊天可以理解为异步通讯,不是实时的进行通讯:时效性差。 同步调用的缺点: 拓展性差(需求不尽提) 性能下降 级联失败 …

实时高清无延迟:EasyDSS/EasyCVR无人机直播技术重塑赛事新体验

近日有网友发视频称,在内蒙古呼伦贝尔一景区的马术表演现场,一架无人机擅自在场地上空飞行拍摄。现场工作人员多次制止飞行无果,一名表演者骑马用弓箭将无人机射落。 在数字科技日新月异的今天,无人机直播推流技术以其独特的视角…

在GPU上运行PyTorch

文章目录 1、查看GPU的CUDA版本2、下载CUDA版本3、安装cuDNN4、配置CUDA环境变量5、安装配置Anaconda6、使用Anaconda7、pycharm导入虚拟环境8、安装带GPU的PyTorch⭐9、总结 🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家博主&#x…

字节跳动十年经验老鸟,耗时大半年整理的软件测试面试真题【附答案】

软件测试工程师,和开发工程师相比起来,虽然前期可能不会太深,但是涉及的面还是比较广的。前期面试实习生或者一年左右的岗位,问的也主要是一些基础性的问题比较多。涉及的知识主要有MySQL数据库的使用、Linux操作系统的使用、软件…

回溯算法的去重问题

概述 在利用回溯算法去求子集、排列、组合等问题时,所给数组中如果包含重复元素,需要进行去重操作。常用的去重方法是使用used数组或集合。 对于上述子集、排列、组合等问题的求解方法是将数组转化为树的形式,利用递归和回溯的方法进行求解…

10.1 JSP语言入门

JSP语言入门 目录一、 基础概念1. 什么是JSP?2. 工作原理3. 基本语法 二、 表达式语言(EL)1. 简介2. 语法 三、 JSTL(JSP Standard Tag Library)1. 简介2. 核心标签库3. 常用标签 四、 高级话题1. 会话管理2. 自定义标…

卷积神经网络(一)-LeNet-5

前言 LeNet开启了卷积神经网络的第一枪,这一网络模型由Yann LeCun等人在1998年提出,被视为卷积神经网络的开山之作。 论文地址: http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf 如果打不开就看csdn: https://download.…

LangChain-v0.2 Build an Agent 构建代理

语言模型本身不能采取行动,它们只是输出文本。LangChain的一个重要用例是创建代理。代理是使用LLM作为推理引擎来确定要采取哪些行动,以及传递哪些输入的系统。执行操作后,可以将结果反馈到LLM中,以确定是否需要更多操作&#xff…

陪玩系统小程序模式APP小程序H5系统搭建开发

随着移动互联网的营及和游戏行业的蓬轨发展,陪玩服务应远而生并迅速唱起,陪玩系统小程序作为连接游戏玩家与陪玩师的桥梁,其模式系统的搭建与开发是得尤为重要,本文将洋细凰述陪玩系统小程宗模式系统的搭建开发流程,包…

基础动态规划题目基础动态规划题目

目录 题目1: P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles 代码示例: 题目2: Common Subsequence 代码示例 题目3 :最长上升子序列 最长不下降子序列 最长上升子序列oj答案 题目1: P1216 [USACO1.5]…

Linux热键,shell含义及权限介绍

君子忧道不忧贫。 —— 孔丘 Linux操作系统的权限 1、几个常用的热键介绍1、1、[Tab]键1、2、[ctrl]-c1、3、[ctrl]-d1、4、[ctrl]-r 2、shell命令以及运行原理3、权限3、1、什么是权限3、2、权限的本质3、3、Linux中的用户3、4、Linux中文件的权限3、4、1、快速掌握修改权限的…

华为HCIP Datacom H12-821 卷41

1.多选题 以下关于BGP Atomic_Aggregate和Aggregator的描述,正确的是哪些项? A、Aggregator属性属于可选过渡属性 B、Atomic_Aggregate属于公认任意属性 C、收到携带Atomic_Aggregate属性的路由表示这条路由不能再度明细化 D、 Agregator表示某条路由可能出现…

古玻璃制品的成分分析与鉴别详解【国一,附完整代码】

声明:2024年数模国赛即将来临,为助力国赛和钉钉杯,我将重温22年小样本国赛C题和23年大样本国赛C题,给出详细思路和完整代码,供广大数模爱好者阅览,如需比赛指导,请联系文章底部卡片咨询。 未经允…