当前位置: 首页 > news >正文

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-腾讯云开发者社区-腾讯云 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【C语言】全面解析冒泡排序
  • vscode通过ssh链接远程服务器上的docker
  • 基于深度学习的车距检测系统
  • vi 编辑器快捷生成 main 函数和基本框架
  • python的readline()和readlines()
  • Hadoop基础组件介绍!
  • 【Git】Git Submodules 介绍(通俗易懂,总结了工作完全够用的 submodule 命令)
  • 签名优化:请求数据类型不是`application/json`,将只对随机数进行签名计算,例如文件上传接口。
  • 网络编程-TCP 协议的三次握手和四次挥手做了什么
  • Spark安装
  • npm安装依赖包报错,npm ERR! code ENOTFOUND
  • 介绍下项目的架构
  • 【精简版】jQuery 中的 Ajax 详解
  • 大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】
  • 如何用EXCEL自动解方程/方程组?利用 矩阵乘法X=A-*B,X=mmult(minverse(A), B)
  • 网络传输文件的问题
  • .pyc 想到的一些问题
  • “大数据应用场景”之隔壁老王(连载四)
  • CentOS 7 修改主机名
  • CODING 缺陷管理功能正式开始公测
  • Java 网络编程(2):UDP 的使用
  • node.js
  • react 代码优化(一) ——事件处理
  • session共享问题解决方案
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • Vue 2.3、2.4 知识点小结
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 分布式熔断降级平台aegis
  • 和 || 运算
  • 浅谈web中前端模板引擎的使用
  • 实习面试笔记
  • 由插件封装引出的一丢丢思考
  • 阿里云移动端播放器高级功能介绍
  • # wps必须要登录激活才能使用吗?
  • (BFS)hdoj2377-Bus Pass
  • (不用互三)AI绘画工具应该如何选择
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (算法)硬币问题
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • . Flume面试题
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET Micro Framework 4.2 beta 源码探析
  • .net2005怎么读string形的xml,不是xml文件。
  • .NET大文件上传知识整理
  • .net经典笔试题
  • .NET微信公众号开发-2.0创建自定义菜单
  • .NET性能优化(文摘)
  • .NET业务框架的构建
  • .NET中分布式服务
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理