springboot 重新注册 bean

项目中,有时候会遇到这样的需求:更新配置后,需要重新处理相关的业务,但是不想重启应用。例如 elasticsearch 证书过期后,需要更换 http_ca.crt ,但是又不想重启应用。

本人对 spring IOC 的源码不算深入,只知道可以实现,捣鼓了大半天,终于实现了,特意记录下过程。写个 demo ,希望有需要的人可以参考一下。

使用 @Bean 来创建注册业务逻辑 bean。当配置更新后,在监听事件里,创建新的业务逻辑 bean,处理业务逻辑,然后使用 beanFactory 销毁旧的业务逻辑 bean 后再重新注册新的业务逻辑bean 

本人使用的是 nacos 配置中心,这里没有给出 helloWorld.yaml 文件,请自行创建

ConfigCenterConfigRefreshed


import com.alibaba.fastjson2.JSONObject;
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.context.ApplicationListener;import java.lang.reflect.Field;/*** nacos 配置中心的配置文件修改的事件监听。* 注意:只有启用自动刷新的配置才会收到配置中心文件修改从而才会触发事件。* @Author: wuYaFang* @Date: 2022/5/5*/
public interface ConfigCenterConfigRefreshed extends ApplicationListener<RefreshEvent> {String getDataId();String getGroup();@Overridedefault void onApplicationEvent(RefreshEvent event){Object source1 = event.getSource();if (source1 == null || !(source1 instanceof AbstractSharedListener)) {return;}AbstractSharedListener source = (AbstractSharedListener) event.getSource();Field[] declaredFields = AbstractSharedListener.class.getDeclaredFields();JSONObject obj = new JSONObject();for (int i = 0; i < declaredFields.length; i++) {try {Field field = declaredFields[i];field.setAccessible(true);Object o = field.get(source);obj.put(field.getName(), o);field.setAccessible(false);} catch (IllegalAccessException ex) {ex.printStackTrace();return;}}Source source2 = obj.toJavaObject(Source.class);if (!StringUtils.equals(source2.getDataId(), getDataId()) || !StringUtils.equals(source2.getGroup(), getGroup())) {return;}refreshed(source2);}/*** 配置更新之后的处理。* @param source*/default void refreshed(Source source) {}@Dataclass Source {private String dataId;private String group;}
}

HelloWorldConfig

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "hello", ignoreInvalidFields = true)
public class HelloWorldConfig {private String name;
}

HelloWorldLogic

package com.demo;import lombok.Data;
import lombok.extern.slf4j.Slf4j;@Data
@Slf4j
public class HelloWorldLogic {private HelloWorldConfig helloWorldConfig;public void say() {log.info("hello-------------{}", helloWorldConfig);}
}

HelloWorldConfiguration 

package com.demo;import com.hmc.cloud.util.api.ConfigCenterConfigRefreshed;
import com.hmc.common.util.HmcSpringBeanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class HelloWorldConfiguration implements ConfigCenterConfigRefreshed {@Autowiredprivate DefaultListableBeanFactory beanFactory;@Autowiredprivate HelloWorldConfig helloWorldConfig;@Beanpublic HelloWorldLogic helloWorldLogic() {return createHelloWorldLogic();}public HelloWorldLogic createHelloWorldLogic() {HelloWorldLogic logic = new HelloWorldLogic();return logic;}@Overridepublic String getDataId() {return "helloWorld.yaml";}@Overridepublic String getGroup() {return "DEFAULT";}@Overridepublic void refreshed(Source source) {// 在 nacos 中修改发布 helloWorld.yaml 之后,更新了 HelloWorldConfig 之后,会调用 此方法 refreshed(Source source)// 重新创建 HelloWorldLogic 实例HelloWorldLogic logic = createHelloWorldLogic();// 处理业务逻辑logic.setHelloWorldConfig(helloWorldConfig);logic.say();// 重新注册到 IOC 容器中, 然后会将新的 helloWorld 实例更新到 HelloWorldController 中。// 需要注意,需要在  HelloWorldController 类上声明 @RefreshScopeHmcSpringBeanUtil.registerSingleton("helloWorldLogic", logic, beanFactory);}
}

HelloWorldController

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RefreshScope
@RestController
public class HelloWorldController {@Autowiredprivate HelloWorldLogic helloWorldLogic;@GetMapping("/test/helloWorld")public HelloWorldConfig helloWorld() {helloWorldLogic.say();return helloWorldLogic.getHelloWorldConfig();}
}

HmcSpringBeanUtil

import org.springframework.beans.factory.config.NamedBeanHolder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;/*** spring bean 的辅助工具* @author wuYaFang* @date 2024/7/17 21:23*/
public class HmcSpringBeanUtil {/*** <pre>* 注册单例Bean* 注意:重新注册 bean 之后,如果依赖者需要声明有 {@link org.springframework.cloud.context.config.annotation.RefreshScope} 才会更新注入** 参考来源:https://cloud.tencent.com/developer/article/2393796* </pre>* @param beanName        名称* @param singletonObject 实例对象*/public static void registerSingleton(String beanName, Object singletonObject, DefaultListableBeanFactory beanFactory) {// 如果已经存在,则先销毁if (beanFactory.containsSingleton(beanName)) {unregisterSingleton(beanName,beanFactory);}beanFactory.registerSingleton(beanName, singletonObject);}/*** <pre>* 注册单例Bean* 注意:重新注册 bean 之后,如果依赖者需要声明有 {@link org.springframework.cloud.context.config.annotation.RefreshScope} 才会更新注入** 参考来源:https://cloud.tencent.com/developer/article/2393796* </pre>* @param beanClass       类* @param singletonObject 实例对象*/public static void registerSingleton(Class<?> beanClass, Object singletonObject, DefaultListableBeanFactory beanFactory) {String beanName = beanClass.getName();// 如果已经存在,则先销毁if (beanFactory.containsSingleton(beanName)) {unregisterSingleton(beanClass, beanFactory);}RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(beanClass);NamedBeanHolder<?> namedBeanHolder = beanFactory.resolveNamedBean(beanClass);beanFactory.registerBeanDefinition(namedBeanHolder.getBeanName(), rootBeanDefinition);beanFactory.registerSingleton(beanName, singletonObject);}/*** <pre>* 注销Bean** 参考来源:https://cloud.tencent.com/developer/article/2393796* </pre>* @param beanName* @param beanFactory*/public static void unregisterSingleton(String beanName, DefaultListableBeanFactory beanFactory) {if (beanFactory instanceof DefaultListableBeanFactory) {// 首先确保销毁该bean的实例(如果该bean实例是一个单例的话)if (beanFactory.containsSingleton(beanName)) {beanFactory.destroySingleton(beanName);}// 然后从容器的bean定义注册表中移除该bean定义if (beanFactory.containsBeanDefinition(beanName)) {beanFactory.removeBeanDefinition(beanName);}}}/*** <pre>* 注销Bean* 参考来源:https://cloud.tencent.com/developer/article/2393796* @param beanClass 类*/public static void unregisterSingleton(Class<?> beanClass, DefaultListableBeanFactory beanFactory) {String beanName = beanClass.getName();if (beanFactory instanceof DefaultListableBeanFactory) {// 首先确保销毁该bean的实例(如果该bean实例是一个单例的话)if (beanFactory.containsSingleton(beanName)) {beanFactory.destroySingleton(beanName);}// 然后从容器的bean定义注册表中移除该bean定义if (beanFactory.containsBeanDefinition(beanName)) {beanFactory.removeBeanDefinition(beanName);}}}
}

bootstrap yaml 配置

spring:cloud:nacos:config:server-addr: ${nacos.config.server-addr}username: ${nacos.config.username}password: ${nacos.config.password}namespace:  ${nacos.config.namespace}enable-remote-sync-config: ongroup: ${model}file-extension: yamlprefix: ${model}extension-configs:- dataId: helloWorld.yamlgroup: hmc-globalrefresh: true

参考:SpringBoot动态注册与更新IOC中的Bean-腾讯云开发者社区-腾讯云 

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

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

相关文章

NodeJS技巧:在循环中管理异步函数的执行次数

背景介绍 在现代Web开发中&#xff0c;NodeJS因其高效的异步处理能力而备受青睐。尤其在数据抓取、网络爬虫等应用场景中&#xff0c;NodeJS的非阻塞I/O特性使其成为不二之选。然而&#xff0c;在实际编程过程中&#xff0c;我们经常会遇到一个棘手的问题——如何在循环中控制…

HTC 10 刷系统 LineageOS 19.1 Android 12

解锁手机 解锁或导致数据全部清除&#xff0c;注意保存 Bootloader解锁&#xff0c;S-ON可以不用解锁&#xff08;好像可以绕过解锁安装twrp&#xff0c;暂时没尝试&#xff09; HTC 官方 Unlock Bootloader HTC Desire 20 pro 可以不通过官方网站解锁 adb reboot bootload…

Unity入门之重要组件和API(4) : Screen

Screen类主要处理屏幕相关的操作。 1.静态属性 1.1常用属性 【设备分辨率】 Resolution resolution Screen.currentResolution; print("设备分辨率宽&#xff1a;" resolution.width " 高&#xff1a;" resolution.height);【屏幕窗口的宽高】 这里…

Spring源码-读取XML文件配置信息

一、XML基础 DTD、XML阐述、XML的两种文档类型约束和DTD的使用-CSDN博客 二、new File()、getResource()、getResourceAsStream() Java中Class类和 ClassLoader 类 的 getResource()和 getResourceAsStream()方法的使用_getclassloader().getresourceasstream-CSDN博客 三、…

svn ldap认证临时切换到本地认证

当前的svn是在CentOS 7 下 SVN、 Apache 对接 LDAP 服务实现用户账号管理和权限认证&#xff0c;本文模拟ldap数据丢失如何恢复svn&#xff0c;方法是临时将认证切换到本地认证 编辑subversion.conf文件 vi /etc/httpd/conf.d/subversion.conf 注释ldap-status #<Locati…

各地跨境电子商务示范区工具变量DID数据(2010-2022年)

数据来源&#xff1a;参考李震等&#xff08;2023&#xff09;的做法&#xff0c;从官方网站上搜集整理了我国跨境电子商务示范区名单与上市公司进行匹配制作。时间跨度&#xff1a;2010-2022年数据范围&#xff1a;上市企业包含指标&#xff1a; stock year 证券简称 In…

高项-信息系统治理知识要点

1、IT 治理由组织治理层或高级管理层负责&#xff1b; IT 治理强调数字目标与组织战略目标保持一致&#xff1b;IT 治理是一种 制度和机制。 2、IT 治理层次分为三层&#xff1a;最高管理层、执行管理层、业务与服务执行层 ◆ 最高管理层&#xff1a;证 实IT 战略与业务战略是否…

个性化画册制作方法,快来看看

画册&#xff0c;不仅仅是一本书&#xff0c;它是记忆的宝库&#xff0c;是时光的缩影。随着技术的进步&#xff0c;个性化画册的制作已经不再是一件困难的事情。今天&#xff0c;就让我来为大家揭开个性化画册制作的神秘面纱&#xff0c;带你一起创造独一无二的回忆。 第一步&…

数据库内核研发学习之路(三)创建postgres内置函数

本章之前已经讲明白了我们的postgres如何进行编译安装&#xff0c;这是很重要的一步&#xff0c;接下来就是学会对postgres进行小的改动&#xff0c;然后保证依然能够顺利编译安装运行&#xff01; 本章续讲内容如何创建一个内置函数。 1、内置函数和用户自定义函数的区别 熟…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【查询密钥别名集(ArkTS)】

查询密钥别名集(ArkTS) HUKS提供了接口供应用查询密钥别名集。 开发步骤 初始化密钥属性集。用于查询指定密钥别名集TAG&#xff0c;TAG仅支持HUKS_TAG_AUTH_STORAGE_LEVEL。调用接口[listAliases]&#xff0c;查密钥别名集。 HarmonyOS与OpenHarmony鸿蒙文档籽料&#xff1…

入门小结:JavaScript小白语法

一、变量 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head> &…

Linux驱动开发中设备节点、虚拟节点、逻辑节点之间的区别与关系

概述 在Linux DTS中我们可以看到各种各样的节点&#xff0c;每个节点都是对某一物理设备或功能抽象或具体的描述 设备节点 设备节点是对物理设备的一种具体的描述&#xff0c;它一般包含设备的寄存器地址、设备的类型、中断、时钟频率这些通用信息&#xff0c;除了这些通用信…

Boost中线程的使用

目录 boost的线程基本用法 boost:condition thread_group 线程组 thread_pool boost的线程基本用法 boost::thread Thread_GenerateUuid;boost::thread Thread_ShowUuid;boost::mutex mutex;std::queue<std::string>UuidQueue;void procGenerateUuid();void showUuid…

vmware_虚拟机安装zabbix_超快超简单

TIPS: 一开始用docker 和 安装包&#xff0c;安装zabbix总是有问题&#xff0c;后发现zabbix官方提供了装好的虚拟机 1、下载VMware pro 个人免费版 官网地址如下 https://support.broadcom.com/group/ecx/productdownloads?subfamilyVMwareWorkstationPro 如果提示注册&am…

服务器数据恢复—开盘修复raid5阵列硬盘故障的数据恢复案例

服务器存储数据恢复环境&#xff1a; 某品牌P2000存储&#xff0c;存储中有一组由8块硬盘&#xff08;包含一块热备盘&#xff09;组建的raid5阵列。上层部署VMWARE ESX虚拟化平台。 服务器存储故障&#xff1a; 存储在运行过程中有两块硬盘指示灯亮黄色。经过运维人员的初步检…

《轻奢,你真的懂吗?》揭秘轻奢概念的营销策略与消费者心理!

有人说奢侈品是一种浪费&#xff0c;但COCO CHANEL却说&#xff1a;“奢华从不与贫穷对立&#xff0c;它的反面是庸俗” “轻奢”这个概念从不是伪概念&#xff0c;它反映了现代社会中消费者对于品质和风格的一种追求&#xff0c;当然在营销学里来说&#xff0c;也反映了市场细…

Ceph资源池pool管理

完成了 Ceph 集群的部署&#xff0c;但是我们如何向 Ceph 中存储数据呢&#xff1f;首先我们需要在 Ceph 中定义一个 Pool 资源池。Pool 是 Ceph 中存储 Object 对象抽象概念。我们可以将其理解为 Ceph 存储上划分的逻辑分区&#xff0c;Pool 由多个 PG 组成&#xff1b;而 PG …

Go语言中GC(垃圾回收回收机制)三色标记与混合写屏障

5、Golang三色标记混合写屏障GC模式全分析 (yuque.com) 第1讲-课程目标_哔哩哔哩_bilibili Golang三色标记GC混合写屏障 Go V1.3之前的标记清除&#xff08;mark and sweep) 垃圾回收、内存管理、自动适放、三色标记法、STW (stop the world) 图的遍历&#xff1f;可达性分…

【Git】(基础篇二)—— Git操作

Git操作 在了解git理论知识之后&#xff0c;本文将结合实践操作为你讲解git的底层逻辑 Git的安装和配置 git官网下载&#xff1a;https://git-scm.com/ 下载后安装时除了选择安装地址外&#xff0c;其余都保持默认下一步即可。 安装好后鼠标右键会出现两个新的选项【Open …

使用Elasticsearch与Java进行全文搜索

全文搜索是现代应用中不可或缺的功能之一&#xff0c;它允许用户通过关键词快速找到所需的信息。Elasticsearch是一个基于Lucene库的分布式搜索和分析引擎&#xff0c;它提供了强大的全文搜索功能。本文将详细介绍如何使用Java与Elasticsearch进行全文搜索&#xff0c;并提供详…