手写分布式配置中心(六)整合springboot(自动刷新)

对于springboot配置自动刷新,原理也很简单,就是在启动过程中用一个BeanPostProcessor去收集需要自动刷新的字段,然后在springboot启动后开启轮询任务即可。
不过需要对之前的代码再次做修改,因为springboot的配置注入@value("${}"),允许多个${}和嵌套,所以不能确定简单的确定用到了那个配置,本文为了简单就把所有的配置都认为需要动态刷新,实际用的时候可以在application.yml中配置需要动态刷新的配置id列表。代码在https://gitee.com/summer-cat001/config-center。其中设计到的原理都在之前的一篇文章中,感兴趣可以去看看springboot配置注入增强(二)属性注入的原理_springboot bean属性增强-CSDN博客

新增注解

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigRefresh {
}

加上这个注解的字段并且字段上有@value注解就会自动刷新

收集自动刷新的字段

这里会收集自动刷新的字段,并加到ConfigCenterClient的refreshFieldValueList中。长轮询会从这里取数据进行对比,如果发生变化就更新bean中的字段

@Slf4j
public class ConfigRefreshAnnotationBeanPostProcessor implements ApplicationRunner, BeanPostProcessor, BeanFactoryAware, EnvironmentAware {private Environment environment;private ConfigurableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (!(beanFactory instanceof ConfigurableBeanFactory)) {log.warn("ConfigurableBeanFactory requires a ConfigurableListableBeanFactory");return;}this.beanFactory = (ConfigurableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInitialization(Object bean, final String beanName) throws BeansException {if (beanFactory != null) {ReflectionUtils.doWithFields(bean.getClass(), field -> {try {ConfigRefresh configRefresh = AnnotationUtils.getAnnotation(field, ConfigRefresh.class);if (configRefresh == null) {return;}Value valueAnnotation = AnnotationUtils.getAnnotation(field, Value.class);if (valueAnnotation == null) {return;}String value = valueAnnotation.value();String relValue = beanFactory.resolveEmbeddedValue(value);ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.addRefreshFieldValue(bean, field, relValue);} catch (Exception e) {log.error("set bean field fail,beanName:{},fieldName:{}", bean.getClass().getName(), field.getName(), e);}});}return bean;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void run(ApplicationArguments args) {ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.startSpringBootLongPolling((ConfigurableEnvironment) environment, beanFactory);}
}

把该bean注入到springboot中,即在spring.factories中加入自动注入

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.config.center.autoconfigure.ConfigAutoConfiguration

这是一个ImportSelector会自动注入返回的类

@Import(ConfigAutoConfiguration.class)
public class ConfigAutoConfiguration implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{ConfigRefreshAnnotationBeanPostProcessor.class.getName()};}
}

启动长轮询

springboot启动完成后会发一个ApplicationRunner事件,我们只要在实现这个接口的bean中启动即可

@Overridepublic void run(ApplicationArguments args) {ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.startSpringBootLongPolling((ConfigurableEnvironment) environment, beanFactory);}
    public void startSpringBootLongPolling(ConfigurableEnvironment environment, ConfigurableBeanFactory beanFactory) {if (configMap.isEmpty() || refreshFieldValueList.isEmpty()) {log.info("configMap.size:{} refreshFieldValueList.size:{}", configMap.size(), refreshFieldValueList.size());return;}MutablePropertySources propertySources = environment.getPropertySources();MapPropertySource configCenter = (MapPropertySource) propertySources.get(PROPERTY_SOURCE_NAME);if (configCenter == null) {log.warn("configCenter is null");return;}Map<String, Object> source = configCenter.getSource();Thread thread = new Thread(() -> {while (!Thread.interrupted()) {try {Map<String, Integer> configIdMap = configMap.values().stream().collect(Collectors.toMap(c -> c.getId() + "", ConfigBO::getVersion));HttpRespBO httpRespBO = HttpUtil.httpPostJson(url + "/config/change/get/long", JSON.toJSONString(configIdMap), 30000);List<ConfigVO> configList = httpResp2ConfigVOList(httpRespBO);if (configList.isEmpty()) {continue;}configList.forEach(configVO -> {Map<String, Object> result = new HashMap<>();DataTransUtil.buildFlattenedMap(result, configVO.getConfigData(), "");ConfigBO configBO = this.configMap.get(configVO.getId());configBO.setVersion(configVO.getVersion());List<ConfigDataBO> configDataList = configBO.getConfigDataList();Map<String, ConfigDataBO> configDataMap = configDataList.stream().collect(Collectors.toMap(ConfigDataBO::getKey, Function.identity()));result.forEach((key, value) -> {ConfigDataBO configDataBO = configDataMap.get(key);if (configDataBO == null) {configDataList.add(new ConfigDataBO(key, value.toString()));} else {configDataBO.setValue(value.toString());source.put(key, value);}});});refreshFieldValueList.forEach(refreshFieldBO -> {try {Field field = refreshFieldBO.getField();Value valueAnnotation = AnnotationUtils.getAnnotation(field, Value.class);if (valueAnnotation == null) {return;}String value = valueAnnotation.value();String relValue = beanFactory.resolveEmbeddedValue(value);if(relValue.equals(refreshFieldBO.getValue())){return;}field.setAccessible(true);field.set(refreshFieldBO.getBean(), relValue);} catch (Exception e) {log.error("startSpringBootLongPolling set Field error", e);}});} catch (Exception e) {log.error("startSpringBootLongPolling error", e);}}});thread.setName("startSpringBootLongPolling");thread.setDaemon(true);thread.start();}

效果

@Value

@Data
@Component
public class ConfigTest {@ConfigRefresh@Value("${user.name}")private String name;}
    @Autowiredprivate ConfigTest configTest;@Testpublic void configTest() throws InterruptedException {while (true) {System.out.println(configTest.getName());Thread.sleep(1000);}}

@ConfigurationProperties

增加同时有@ConfigurationProperties和@ConfigRefresh的收集

ConfigRefresh configRefresh = AnnotationUtils.findAnnotation(bean.getClass(), ConfigRefresh.class);if (configRefresh != null) {ConfigurationProperties configurationProperties = AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class);if (configurationProperties != null) {ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.addRefreshBeanList(bean);}}

在长轮询的返回中对@ConfigurationProperties重新绑定

refreshBeanList.forEach(refreshBean -> {ConfigurationProperties configurationProperties = AnnotationUtils.findAnnotation(refreshBean.getClass(), ConfigurationProperties.class);if (configurationProperties == null) {log.warn("refreshBeanList refreshBean configurationProperties is null, class:{}", refreshBean.getClass());return;}Binder binder = Binder.get(environment);binder.bind(configurationProperties.prefix(), Bindable.ofInstance(refreshBean));});

完整代码

@Slf4j
public class ConfigRefreshAnnotationBeanPostProcessor implements ApplicationRunner, BeanPostProcessor, BeanFactoryAware, EnvironmentAware {private Environment environment;private ConfigurableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (!(beanFactory instanceof ConfigurableBeanFactory)) {log.warn("ConfigurableBeanFactory requires a ConfigurableListableBeanFactory");return;}this.beanFactory = (ConfigurableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInitialization(Object bean, final String beanName) throws BeansException {if (beanFactory != null) {ReflectionUtils.doWithFields(bean.getClass(), field -> {try {ConfigRefresh configRefresh = AnnotationUtils.getAnnotation(field, ConfigRefresh.class);if (configRefresh == null) {return;}Value valueAnnotation = AnnotationUtils.getAnnotation(field, Value.class);if (valueAnnotation == null) {return;}String value = valueAnnotation.value();String relValue = beanFactory.resolveEmbeddedValue(value);ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.addRefreshFieldValue(bean, field, relValue);} catch (Exception e) {log.error("set bean field fail,beanName:{},fieldName:{}", bean.getClass().getName(), field.getName(), e);}});ConfigRefresh configRefresh = AnnotationUtils.findAnnotation(bean.getClass(), ConfigRefresh.class);if (configRefresh != null) {ConfigurationProperties configurationProperties = AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class);if (configurationProperties != null) {ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.addRefreshBeanList(bean);}}}return bean;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void run(ApplicationArguments args) {ConfigCenterClient configCenterClient = ConfigCenterClient.getInstance(null);configCenterClient.startSpringBootLongPolling((ConfigurableEnvironment) environment, beanFactory);}
}
 public void startSpringBootLongPolling(ConfigurableEnvironment environment, ConfigurableBeanFactory beanFactory) {if (configMap.isEmpty() || refreshFieldValueList.isEmpty()) {log.info("configMap.size:{} refreshFieldValueList.size:{}", configMap.size(), refreshFieldValueList.size());return;}MutablePropertySources propertySources = environment.getPropertySources();MapPropertySource configCenter = (MapPropertySource) propertySources.get(PROPERTY_SOURCE_NAME);if (configCenter == null) {log.warn("configCenter is null");return;}Map<String, Object> source = configCenter.getSource();Thread thread = new Thread(() -> {while (!Thread.interrupted()) {try {Map<String, Integer> configIdMap = configMap.values().stream().collect(Collectors.toMap(c -> c.getId() + "", ConfigBO::getVersion));HttpRespBO httpRespBO = HttpUtil.httpPostJson(url + "/config/change/get/long", JSON.toJSONString(configIdMap), 30000);List<ConfigVO> configList = httpResp2ConfigVOList(httpRespBO);if (configList.isEmpty()) {continue;}configList.forEach(configVO -> {Map<String, Object> result = new HashMap<>();DataTransUtil.buildFlattenedMap(result, configVO.getConfigData(), "");ConfigBO configBO = this.configMap.get(configVO.getId());configBO.setVersion(configVO.getVersion());List<ConfigDataBO> configDataList = configBO.getConfigDataList();Map<String, ConfigDataBO> configDataMap = configDataList.stream().collect(Collectors.toMap(ConfigDataBO::getKey, Function.identity()));result.forEach((key, value) -> {ConfigDataBO configDataBO = configDataMap.get(key);if (configDataBO == null) {configDataList.add(new ConfigDataBO(key, value.toString()));} else {configDataBO.setValue(value.toString());source.put(key, value);}});});refreshFieldValueList.forEach(refreshFieldBO -> {try {Field field = refreshFieldBO.getField();Value valueAnnotation = AnnotationUtils.getAnnotation(field, Value.class);if (valueAnnotation == null) {return;}String value = valueAnnotation.value();String relValue = beanFactory.resolveEmbeddedValue(value);if (relValue.equals(refreshFieldBO.getValue())) {return;}field.setAccessible(true);field.set(refreshFieldBO.getBean(), relValue);} catch (Exception e) {log.error("startSpringBootLongPolling set Field error", e);}});refreshBeanList.forEach(refreshBean -> {ConfigurationProperties configurationProperties = AnnotationUtils.findAnnotation(refreshBean.getClass(), ConfigurationProperties.class);if (configurationProperties == null) {log.warn("refreshBeanList refreshBean configurationProperties is null, class:{}", refreshBean.getClass());return;}Binder binder = Binder.get(environment);binder.bind(configurationProperties.prefix(), Bindable.ofInstance(refreshBean));});} catch (Exception e) {log.error("startSpringBootLongPolling error", e);}}});thread.setName("startSpringBootLongPolling");thread.setDaemon(true);thread.start();}

效果

@Component
@ConfigRefresh
@ConfigurationProperties(prefix = "user")
public class ConfigTest2 {private String name;private int age;private List<String> education;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public List<String> getEducation() {return education;}public void setEducation(List<String> education) {this.education = education;}
}
   @Autowiredprivate ConfigTest2 configTest2;@Testpublic void configTest() throws InterruptedException {while (true) {System.out.println(configTest2.getName() + "-" + configTest2.getAge() + "-" + configTest2.getEducation());Thread.sleep(1000);}}

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

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

相关文章

C++命名空间详解

目录 C的由来&#xff1a; C语言的命名缺陷&#xff1a; 什么是命名空间&#xff1f; 命名空间的访问方式&#xff1a; 1、指定访问 2、展开命名空间&#xff08;慎用&#xff09; 相关知识&#xff1a; C的由来&#xff1a; C原来本质上是在C的基础上补充的语法&#x…

ARM单片机中程序在ROM空间和RAM空间的分布(分散加载文件,Scatter-Loading Description File)

对于 K e i l u V i s i o n I D E Keil\quad uVision\quad IDE KeiluVisionIDE&#xff0c;程序编译好之后&#xff0c;代码的下载位置&#xff08; R O M ROM ROM空间&#xff09;以及代码运行的时候使用的 R A M RAM RAM空间&#xff08; R A M RAM RAM空间&#xff09;默认…

下载一些ROS的包的方式

ROS Index 我们可以去ROS Index网站下载一些我们需要的包。打开浏览器在网址框输入index.ros.org。或者点击此处链接ROS Index 在这个网站中我们可以浏览并找到我们需要的包&#xff0c;也可以下载它的源代码或者仅安装到我们的系统中来使用。&#xff08;安装过程在终端中进行…

植物病虫害:YOLO水稻虫害识别数据集(6类,五千余张图像,标注完整)

YOLO水稻虫害识别数据集&#xff0c;包含褐飞虱&#xff0c;绿叶蝉&#xff0c;正常叶片&#xff0c;稻虫&#xff0c;二化螟&#xff0c;蝇蛆6个类别&#xff0c;共五千余张图像&#xff0c;yolo标注完整。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 …

零、自然语言处理开篇

目录 0、NLP任务的基础——符号向量化 0.0 词袋模型 0.1 查表/One-hot编码 0.2 词嵌入模型/预训练模型 0.2.0 Word2Vec &#xff08;0&#xff09;CBOW &#xff08;1&#xff09;Skip-gram 0.2.1 GloVe 0.2.2 WordPiece 0.2.3 BERT 0.2.4 ERNIE NLP自然语言处理&am…

Java 的 System 类常用方法介绍

Java 中的 System 类是一个final类&#xff0c;它提供了与系统相关的属性和方法。它是一个内置的类&#xff0c;可以直接使用&#xff0c;不需要实例化。System 类提供了标准输入、标准输出和错误输出流&#xff0c;以及对外部定义的属性和系统环境的访问。下面是 System 类的一…

LCR 132. 砍竹子 II

解题思路&#xff1a; 由于数量级较大&#xff0c;需要使用long以及快速幂求余 下图便于理解快速幂求余 class Solution {public int cuttingBamboo(int bamboo_len) {if(bamboo_len < 3) return bamboo_len - 1;int b bamboo_len % 3, p 1000000007;long rem 1, x 3;f…

【PHP+代码审计】PHP基础——数据类型

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

012集——显示高考天数倒计时——vba实现

以下代码实现高考倒计时&#xff1a; Sub 高考倒计时() 高考日期 CDate("06,07," & Year(Date)) If Date > 高考日期 Then高考日期 CDate("06-07-" & Year(Date) 1) End If 年月日 Year(Date) & "年" & Month(Date) &am…

Springboot+vue的项目申报管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的项目申报管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09…

低密度奇偶校验码LDPC(九)——QC-LDPC译码器FPGA全并行设计

往期博文 低密度奇偶校验码LDPC&#xff08;一&#xff09;——概述_什么是gallager构造-CSDN博客 低密度奇偶校验码LDPC&#xff08;二&#xff09;——LDPC编码方法-CSDN博客 低密度奇偶校验码LDPC&#xff08;三&#xff09;——QC-LDPC码概述-CSDN博客 低密度奇偶校验码…

蓝牙系列七:开源蓝牙协议栈BTStack数据处理

继续蓝牙系列的研究。 在上篇博客,通过阅读BTStack的源码,大体了解了其框架,对于任何一个BTStack的应用程序都有一个main函数,这个main函数是统一的。这个main函数做了某些初始化之后,最终会调用到应用程序提供的btstack_main,在btstack_main里面首先做一些初始化,然后…

C语言指针——常量字符串和 变量字符串

常量字符串和 变量字符串 常量字符串和变量字符串是在编程中常见的两种字符串类型&#xff0c;它们有以下区别&#xff1a; 值的不可变性&#xff1a;常量字符串的值是不可变的&#xff0c;一旦被定义&#xff0c;就不能修改。而变量字符串的值是可变的&#xff0c;可以随时修…

重建大师6.2版本的建模效果出现下图中模糊的情况,是什么原因?

可能是因为坐标原点设置的不对&#xff0c;图例中的三角网都出现了精度损失的问题。 坐标原点设置的具体操作&#xff1a;提交产品后&#xff0c;在弹出的界面&#xff0c;可以设定坐标原点。 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件&#xff0…

C语言之练手题

题目1&#xff1a; 思路&#xff1a;我们定义两个变量left和right分别为数组的左端下标和右端下标。 左端下标的元素为奇数时&#xff0c;left继续往前走&#xff0c;为偶数时就停下 右端下标的元素为偶数时&#xff0c;right- -往回走&#xff0c;为奇数时停下 停下后对应的元…

(3)应用与信息

文章目录 前言 3.1 FlightDeck FrSky发射器应用程序 3.2 MAVLink2数据包签名(安全) 3.3 MAVLink高延迟协议 3.4 无线地面站连接中继器 1 概述 2 组件 3 设置 3.5 遥测无线电区域条例 3.6 用于OpenTX的Yaapu遥测脚本 前言 FlightDeck FrSky Transmitter AppMAVLink2 …

Xmind拿来主义快速高效,Xmind思维导图模板合集

一、素材描述 本套Xmind模板&#xff0c;大小153.57M&#xff0c;共有310个文件。 二、素材目录 01-学习模版&#xff08;46套模板&#xff09; 02-知识结构&#xff08;88套模板&#xff09; 03-生活计划&#xff08;26套模板&#xff09; 04-商务模版&#xff08;44套模…

就业班 2401--3.8 Linux Day14--阿帕奇+LNMP(编译安装)

一、WEB服务器 ^世上最重要的事&#xff0c;不在于我们在何处&#xff0c;而在于我们朝着什么方向走。^ 1、WEB服务简介 # 目前最主流的三个Web服务器是Apache、Nginx、 IIS。 - WEB服务器一般指网站服务器&#xff0c;可以向浏览器等Web客户端提供网站的访问&#xff0c;让全…

20-Java备忘录模式 ( Memento Pattern )

Java备忘录模式 摘要实现范例 备忘录模式&#xff08;Memento Pattern&#xff09;保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象 备忘录模式属于行为型模式 摘要 1. 意图 在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对…

JVM 的垃圾回收机制以及垃圾回收算法的详解

目录 1、JVM 的垃圾回收机制 2、识别垃圾 2.1、引用计数 2.2、可达性分析 3、垃圾回收算法 3.1、标记-清除 3.2、复制算法 3.3、标记-整理 4、分代回收 1、JVM 的垃圾回收机制 对于&#xfeff;程序计数器&#xfeff;、&#xfeff;虚拟机栈&#xfeff;、&#xfe…