【架构师进阶必备】Spring - 运行时以四种方式动态注册 bean

news2024/12/24 9:11:07

Spring — 运行时以四种方式动态注册 bean

在这里插入图片描述

1. 概述

在本教程中,我们将学习“使用 spring 动态注册 bean”或“在运行时动态将 bean 添加到 spring-context”。在Spring 应用程序中加载和删除 bean 时,无需在运行时重新启动应用程序即可完成此操作。

如果客户端代码需要注册不受 Spring 容器管理的对象,那么我们将需要使用 BeanDefinition 实例。
Spring 应用程序可以使用 BeanDefinitionRegistry 的以下方法来注册BeanDefinition :

void registerBeanDefinition(String beanName,BeanDefinition beanDefinition)

2. Bean定义

BeanDefinition 描述了一个 bean 实例。它具有 setter 方法,可用于以编程方式将 Spring 特定特征设置为 bean,例如,BeanDefinition #setScope(String scope)可用于设置除默认单例之外的范围。

3. GenericBeanDefinition

这是常用的BeanDefinition实现。

它允许指定 bean 类、bean 特性以及构造函数参数值和属性值。

4.动态注册bean

有很多方法可以实现这一点。我们将通过以下示例进行讨论。

  1. 通用Bean定义
  2. BeanDefinitionBuilder
  3. BeanFactoryPostProcessor
  4. BeanDefinitionRegistryPostProcessor

4.1 使用GenericBeanDefinition进行动态Bean注册

GenericBeanDefinition 是标准 bean 定义的一站式解决方案。与任何 bean 定义一样,它允许指定类以及可选的构造函数参数值和属性值。此外,可以通过“ parentName ”属性灵活地配置从父 bean 定义派生。

通常,使用此GenericBeanDefinition类来注册用户可见的 bean 定义(后处理器可能会对其进行操作,甚至可能重新配置父名称)。在父/子关系已预先确定的情况下,请使用RootBeanDefinition / ChildBeanDefinition 。

创建示例 Bean 类。

public class MyBean {
  private Date date;
  public void doSomething () {
      System.out.println("来自我的 bean,日期:" + date);
  }
  public void setDate (Date date) {
      this.date = date;
  }
}

使用GenericBeanDefinition动态注册上述创建的 bean 。

public class GenericBeanDefinitionExample 
{
  public static void main (String[] args) 
  {
      DefaultListableBeanFactory context = new DefaultListableBeanFactory();
      GenericBeanDefinition gbd = new GenericBeanDefinition();
      gbd.setBeanClass(MyBean.class);
      MutablePropertyValues mpv = new MutablePropertyValues();
      mpv.add("date", new Date());
      
      //alternatively we can use:
      // gbd.getPropertyValues().addPropertyValue("date", new Date());
      gbd.setPropertyValues(mpv);
      context.registerBeanDefinition("myBeanName", gbd);
      MyBean bean = context.getBean(MyBean.class);
      bean.doSomething();
  }
}

输出:

来自我的bean,日期:2019723 日星期三 12:20:58 EDT

4.2 使用 BeanDefinitionBuilder 进行动态 Bean 注册

使用构建器模式构建BeanDefinitions的编程方法。主要用于实现 Spring 2.0 NamespaceHandlers。这里唯一的区别是,BeanDefinitionBuilder 使用Builder Pattern。

创建另一个 bean 类。

public class MyBean {
  private String str;
  public void setStr (String str) {
      this.str = str;
  }
  public void doSomething () {
      System.out.println("from MyBean " + str);
  }
}

使用BeanDefinitionBuilder动态注册 bean 的示例。

public class BeanDefinitionBuilderExample {
  public static void main (String[] args) {
      DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
      BeanDefinitionBuilder b = BeanDefinitionBuilder
      			.rootBeanDefinition(MyBean.class)
           .addPropertyValue("str", "myStringValue");
           
      beanFactory.registerBeanDefinition("myBean", b.getBeanDefinition());
      MyBean bean = beanFactory.getBean(MyBean.class);
      bean.doSomething();
  }
}

输出:

from MyBean myStringValue

使用 BeanDefinitionBuilder 注入其他 bean 引用
a. 创建 Bean 1

public class Bean1 {
  private Bean2 otherBean;
  public void setOtherBean (Bean2 otherBean) {
      this.otherBean = otherBean;
  }
  public void doSomething () {
      otherBean.doSomething();
  }
}

b. 创建 Bean 2

public class Bean2 {
  public void doSomething () {
      System.out.println("from other bean ");
  }
}

c. 在 Bean1 中设置 Bean2

public class InjectingOtherBeans {
  public static void main (String[] args) {
      DefaultListableBeanFactory context = new DefaultListableBeanFactory();
      
      //define and register MyOtherBean
      GenericBeanDefinition beanOtherDef = new GenericBeanDefinition();
      beanOtherDef.setBeanClass(Bean2.class);
      context.registerBeanDefinition("other", beanOtherDef);
      
      //definine and register myBean
      GenericBeanDefinition beanDef = new GenericBeanDefinition();
      beanDef.setBeanClass(Bean1.class);
      MutablePropertyValues mpv = new MutablePropertyValues();
      mpv.addPropertyValue("otherBean", context.getBean("other"));
      beanDef.setPropertyValues(mpv);
      context.registerBeanDefinition("myBean", beanDef);
      
      //using MyBean instance
      MyBean bean = context.getBean(MyBean.class);
      bean.doSomething();
  }
}

输出:

from other bean

4.3 使用 BeanFactoryPostProcessor 进行动态 Bean 注册

BeanFactoryPostProcessor可以与 bean 定义交互并修改 bean 实例,但绝不能与 bean 实例交互并修改 bean 定义。允许自定义修改应用程序上下文的 bean 定义,调整上下文底层 bean 工厂的 bean 属性值。应用程序上下文可以自动检测其bean 定义中的BeanFactoryPostProcessor bean,并在创建任何其他 bean 之前应用它们。

创建配置:

@Configuration
public class MyConfig {
  @Bean
  MyConfigBean myConfigBean () {
      return new MyConfigBean();
  }
}

BeanFactoryPostProcessor允许客户端代码自定义 bean 定义。BeanFactoryPostProcessor.postProcessBeanFactory 方法由 Spring 启动过程在加载所有 bean 定义但在尚未实例化任何 bean之前调用。

public class MyConfigBean implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
      GenericBeanDefinition bd = new GenericBeanDefinition();
      bd.setBeanClass(MyBean.class);
      bd.getPropertyValues().add("strProp", "my string property");
      ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("myBeanName", bd);
  }
}

为该 Demo 创建另一个 Bean。

public class MyBean {
  private String strProp;
  public void setStrProp (String strProp) {
      this.strProp = strProp;
  }
  public void doSomething () {
      System.out.println("from MyBean:  " + strProp);
  }
}

创建BeanFactoryPostProcessor 示例的主类并运行。

public class BeanFactoryPostProcessorExample {
  public static void main (String[] args) {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
      MyBean bean = context.getBean(MyBean.class);
      bean.doSomething();
  }
}

输出:

from MyBean:  my string property
WARNING: @Bean method MyConfig.myConfigBean is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.

4.4 使用 BeanDefinitionRegistryPostProcessor 进行动态 Bean 注册

对标准BeanFactoryPostProcessor SPI 的扩展,允许在常规 BeanFactoryPostProcessor 检测启动之前注册进一步的 bean 定义。特别是,BeanDefinitionRegistryPostProcessor可以注册进一步的 bean 定义,而这些 bean 定义又定义BeanFactoryPostProcessor实例。

创建另一个配置类:

@Configuration
public class MyConfig {
  @Bean
  MyConfigBean myConfigBean () {
      return new MyConfigBean();
  }
}

这是BeanFactoryPostProcessor (最后一个例子)的子接口。它允许注册 bean 定义。它的方法postProcessBeanDefinitionRegistry在BeanFactoryPostProcessor#postProcessBeanFactory之前被调用。这个接口更专注于 BeanDefinition 注册,而不是通用的BeanFactoryPostProcessor。

为 BeanDefinitionRegistryPostProcessor 创建实现类。

public class MyConfigBean implements BeanDefinitionRegistryPostProcessor {
  @Override
  public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry)
            throws BeansException {
      GenericBeanDefinition bd = new GenericBeanDefinition();
      bd.setBeanClass(MyBean.class);
      bd.getPropertyValues().add("strProp", "my string property");
      registry.registerBeanDefinition("myBeanName", bd);
  }
  @Override
  public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
      //no op
  }
}

创建一个全新的 Bean 类。

public class MyBean {
  private String strProp;
  public void setStrProp (String strProp) {
      this.strProp = strProp;
  }
  public void doSomething () {
      System.out.println("from MyBean:  " + strProp);
  }
}

创建BeanDefinitionRegistryPostProcessor 示例的主类并运行:

public class BeanDefinitionRegistryPostProcessorExample {
  public static void main (String[] args) {
      AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(MyConfig.class);
      MyBean bean = (MyBean) context.getBean("myBeanName");
      bean.doSomething();
  }
}

输出:

from MyBean:  my string property
WARNING: Cannot enhance @Configuration bean definition 'beanDefinitionRegistryPostProcessorExample.MyConfig' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.

5. 运行时注销 Bean

如果我们需要在运行时删除已注册的bean,我们可以按照下面的方法操作。

从 spring 上下文中删除或者取消注册该 bean。

beanRegistry.removeBeanDefinition("bean")

从上下文中删除/清除单例 bean。

beanRegistry.destroySingleton("bean")

6. 结论

在本文中,我们了解了如何在 Spring 框架中动态注册一个 bean。

本文详细展示了 GenericBeanDefinition、BeanDefinitionBuilder、BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的示例程序。希望对大家有所帮助,感谢大家拜读~~!

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

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

相关文章

先用先发!小样本故障诊断新思路!Transformer-SVM组合模型多特征分类预测/故障诊断(Matlab)

先用先发!小样本故障诊断新思路!Transformer-SVM组合模型多特征分类预测/故障诊断(Matlab) 目录 先用先发!小样本故障诊断新思路!Transformer-SVM组合模型多特征分类预测/故障诊断(Matlab&#…

【RabbitMQ】路由模式(Routing)

一、基本概念 生产者(Producer):发送消息到交换机的程序。在发送消息时,需要指定一个路由键。交换机(Exchange):接收生产者发送的消息,并根据路由键将消息路由到与之匹配的队列。在…

【C++BFS】1466. 重新规划路线

本文涉及知识点 CBFS算法 LeetCode1466. 重新规划路线 n 座城市,从 0 到 n-1 编号,其间共有 n-1 条路线。因此,要想在两座不同城市之间旅行只有唯一一条路线可供选择(路线网形成一颗树)。去年,交通运输部…

软件测试--易用性测试

人体工程学这是一门将日常使用的东西设计为易于使用何实用性强的科学。因此人体工程学的主要目标是达到易用性。 用户界面测试 用于与软件程序交互的方式称为用户界面或UI。大家都熟悉的计算机UI随着时间推移发生了变化。早期的计算机有触发开关和发光管。纸带、穿孔卡和电传打…

AIGC技术的未来航向:深度解析与Java实践

摘要: 本文深入探讨了人工智能生成内容(AIGC)技术的未来发展方向,从技术创新、可持续可拓展性、用户体验、应用场景、政府赋能等多维度进行分析,并结合Java技术实践,提供具体的实现策略和代码示例。 引言…

PDF翻译神器:这四款可以实现一键搞定,留学党必备!

外文的阅读还是需要一定的语言功底,现在大家也对外文越来越重视起来了,但是借助一些翻译工具进行翻译可以很大程度地提升工作的效率,就算是遇到批量的文件处理也可以一键翻译出来,所以今天借此文章整理了四款好用的pdf翻译工具&am…

计算机基础(Windows 10+Office 2016)教程 —— 第3章 操作系统基础(下)

操作系统基础 Windows 10的系统管理3.5.1 设置日期和时间3.5.2 Windows 10 个性化设置3.5.3 安装和卸载应用程序3.5.4 分区管理3.5.5 格式化磁盘3.5.6 清理磁盘 3.6 Windows 10的网络功能3.6.1 网络软硬件的安装3.6.2 查看网络中其他计算机3.6.3 资源共享 3.7 Windows 10系统的…

数据灾备及时恢复应急预案

第一节总则 1,灾难备份的目的 为了规范本所重要数据备份清单的建立,备份的职责,备份的检查。以及系统受到破坏后的恢复工作,合理防范计算机及信息系统使用过程中的风险,特制定本预案。 2,灾难恢复的定义 灾…

1.kafka面试题之零拷贝

1. 写在前面 Kafka 是一个高性能的分布式消息系统,它使用了多种优化技术来提高数据传输效率,其中之一就是 “零拷贝”(Zero Copy)。零拷贝技术可以显著减少数据在内存中的复制次数,从而提高 I/O 操作的效率&#xff0…

模拟栈解决表达式求值-java

主要讲述了通过栈来解决后缀表达式,来计算出表达式的结果,可以好好熟悉一下思路。 目录 前言 一、表达式求值问题 二、栈模拟计算表达式 1.算法思路 2.代码解释 三、代码实现 1.代码如下: 2.测试样例如下: 3.运行结果如下…

【轨物推荐】经济长波:创新周期的历史

原创 丑丑姐姐 专利分析可视化 2021年08月01日 21:18 图片来源:Visual Capitalist 在开始本文之前,我们先来学习两个概念: 经济长波(Long Waves),亦称“大循环理论”、“康德拉季耶夫周期”。经济长波理论…

redis持久化存储,rdb快照文件,aof文件

redis作为内存数据库,在内存中进行读写操作,将读写操作从毫秒级别降为纳秒级别,得到极大的性能提升,与此同时,作为内存数据库其也有致命缺陷,一旦redis发生意外宕机,那么内存中的数据将全部消失…

智慧医院临床检验管理系统源码(LIS),全套LIS系统源码交付,商业源码,自主版权,支持二次开发

实验室信息系统是集申请、采样、核收、计费、检验、审核、发布、质控、查询、耗材控制等检验科工作为一体的网络管理系统。它的开发和应用将加快检验科管理的统一化、网络化、标准化的进程。一体化设计,与其他系统无缝连接,全程化条码管理。支持危机值管…

如何手动修复DLL丢失?2种手动修复dll文件方法

DLL(动态链接库)文件是Windows操作系统中非常重要的组成部分,它们包含了程序运行所需的代码和数据。然而,由于各种原因,如系统更新、软件卸载不当或病毒感染,DLL文件有时会丢失或损坏,导致程序无…

Python pyautogui 自动控制 MDK Keil_v5 Pack Installer 的 Packs 安装过程

MDK Keil_v5 安装完成后,会自动进行 Pack Installer 的 Packs 安装,安装过程中首先 install 需要一行行用鼠标点,然后每一行的 Pack 都会出现同意安装或连接超时的弹窗,需要鼠标操作确认。 pyautogui 可以帮助自动控制鼠标完成确…

【C++】关于仿函数Functor 的理解和应用

C中的仿函数(Functor):深入理解与应用 仿函数的基本概念仿函数在STL中的应用仿函数的分类STL中的常见仿函数 仿函数的优势结论 在C编程中,仿函数(Functor)是一种特殊的类,它通过重载函数调用运算…

【RabbitMQ】通配符模式(Topics)

一、基本概念 生产者(Producer):发送消息到RabbitMQ交换机的程序。生产者定义消息的路由键,用于标识消息的目的地。交换机(Exchange):接收生产者发送的消息,并根据路由键和绑定规则…

IT运维中,如何快速进行故障排查?(以银行APP交易故障为例)

一、事件背景 正值"五一"黄金周旅游高峰期,某城商行的手机APP突然出现大面积交易失败和严重卡顿现象。据初步统计,从上午10点开始APP的交易成功率从正常的99%骤降至75%左右,用户反馈的交易失败投诉量在短短2小时内激增了500%。与此…

volatile 关键字的两层语义

volatile 关键字的两层语义 1、可见性2、禁止指令重排序3、工作机制4、总结 💖The Begin💖点点关注,收藏不迷路💖 volatile 关键字在Java并发编程中扮演着重要角色,它主要用于保证变量的可见性和禁止指令重排序。 1、…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-63 - Canvas和SVG元素定位

软件测试微信群:https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 今天宏哥分享的在实际测试工作中很少遇到,比较生僻,如果突然遇到我们可能会脑大、懵逼,一时之间不知道怎么办?所以宏哥这里提供…