业务需求

SpringBoot项目配置信息大多使用@Value注解或者@ConfigurationProperties注解读取配置信息,线上项目经常需要对某些配置进行调整,如果每次都需要修改配置文件再重新发布服务,难免会导致服务中断。

尤其是在分布式系统中多个服务节点都需要修改配置文件的场景,基于此配置中心也应运而生。

如果我们的项目使用了SpringCloud,那么可选的配置中心有很多,比如Nacos、spring-cloud-starter-config、Apollo等,这些配置中心都需要借助SpringCloud架构才能实现配置刷新。

这里我们的应用没有集成SpringCloud,也不想因为配置中心而让应用架构变重,所以需要基于SpringBoot基础实现一个轻量级的配置动态刷新功能。

远程配置中心

对配置资源进行管理,所有应用连接配置中心读取配置信息,可选用的配置中心包括:

1、配置中心中间件,例如:Nacos;

2、数据库存储;

3、Git仓库;

4、文件系统;

5、其它具备持久化存储及访问功能的中间件。

说点听得懂的实现原理

想要实现配置动态刷新可以从Spring的Bean初始化和属性值注入原理入手,这里我们跳过原理分析阶段,感兴趣的同学自行百度,建议大家可以直接看下Spring的PropertyPlaceholderAutoConfiguration类。

刷新配置信息仅需简单两步:

1、刷新Spring的Environment环境变量;

2、刷新Spring托管的Bean实例的属性值;

刷新Environment

/**
* 刷新环境变量
* @param properties
*/
private MutablePropertySources refreshEnvironment(Properties properties) {
Map<String, Object> props = new HashMap<>();
properties.stringPropertyNames().stream().forEach(key ->
props.put(key, properties.getProperty(key))
);
// 获取spring的environment
MutablePropertySources mutablePropertySources = environment.getPropertySources();
// 添加远程配置信息
mutablePropertySources.addFirst(new MapPropertySource("remoteConfig", props)); return mutablePropertySources;
}

刷新Bean实例

需要注意一下几点:

1、我们不用自己解析@Value的value,通过Spring提供的PropertySourcesPropertyResolver.resolveRequiredPlaceholders即可从环境变量中获取对应的属性值;

2、@Value可以使用EL表达式,注入的属性类型可以是String、List等对象,通过Spring提供的SpelExpressionParser类实现EL表达式解析和运算取值。

/**
* 刷新Bean实例的属性值
* @param bean
* @param propertyResolver
*/
private void refreshBean(Object bean, ConfigurablePropertyResolver propertyResolver) { // 定义EL表达式解释器
SpelExpressionParser spelExpressionParser;
spelExpressionParser = new SpelExpressionParser();
TemplateParserContext templateParserContext;
templateParserContext = new TemplateParserContext(); String keyResolver, valueResolver = null;
Object parserValue;
// 遍历Bean实例所有属性
for (Field field : bean.getClass().getDeclaredFields()) {
// 判断field是否含有@Value注解
if (field.isAnnotationPresent(Value.class)) {
// 读取Value注解占位符
keyResolver = field.getAnnotation(Value.class).value();
try {
// 读取属性值
valueResolver = propertyResolver.resolveRequiredPlaceholders(keyResolver);
// EL表达式解析
// 兼容形如:@Value("#{'${url}'.split(',')}")含有EL表达式的情况
parserValue = spelExpressionParser.parseExpression(valueResolver, templateParserContext).getValue(field.getType()); } catch (IllegalArgumentException e) {
log.warn("{}", e.getMessage());
continue;
}
// 判断配置项是否存在
if (Objects.nonNull(valueResolver)) {
field.setAccessible(true);
try {
field.set(bean, parserValue);
continue;
} catch (IllegalAccessException e) {
log.error("{}刷新属性值出错, bean: [{}], field: [{}], value: [{}]",
TAG, bean.getClass().getName(), field.getName(), valueResolver);
}
}
}
}
}

手撸一个SpringBoot配置中心实现配置动态刷新的更多相关文章

  1. 手撸一个SpringBoot的Starter,简单易上手

    前言:今天介绍一SpringBoot的Starter,并手写一个自己的Starter,在SpringBoot项目中,有各种的Starter提供给开发者使用,Starter则提供各种API,这样使开发S ...

  2. 看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么我们要去造轮子? 造轮子的核心目的,是为了解决通用共性问题的凝练和复用. 虽然 ...

  3. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

  4. 手撸一个springsecurity,了解一下security原理

    手撸一个springsecurity,了解一下security原理 转载自:www.javaman.cn 手撸一个springsecurity,了解一下security原理 今天手撸一个简易版本的sp ...

  5. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  6. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  7. 五分钟,手撸一个Spring容器!

    大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开S ...

  8. .Net Core 自定义配置源从配置中心读取配置

    配置,几乎所有的应用程序都离不开它..Net Framework时代我们使用App.config.Web.config,到了.Net Core的时代我们使用appsettings.json,这些我们再 ...

  9. spring cloud 系列第8篇 —— config+bus 分布式配置中心与配置热刷新 (F版本)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.config 简介 spring cloud config 分为服务端 ...

  10. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

随机推荐

  1. Sealos 国内集群正式上线,可一键运行 LLama2 中文版大模型!

    2023 年 7 月 19 日,MetaAI 宣布开源旗下的 LLama2 大模型,Meta 首席科学家.图灵奖得主 Yann LeCun 在推特上表示 Meta 此举可能将改变大模型行业的竞争格局. ...

  2. 【page cache】回写机制

    目录 writeback 回写 相关结构体 底层设备信息 初始化 部分字段说明 设备回写管理 初始化 部分字段说明 回写任务 部分字段说明 回写线程 初始化 立即唤醒 wb_wakeup wb_que ...

  3. 文心一言 VS 讯飞星火 VS chatgpt (81)-- 算法导论7.4 6题

    六.如果用go语言,考虑对 PARTITION 过程做这样的修改:从数组 A 中随机选出三个元素,并用这三个元素的中位数(即这三个元素按大小排在中间的值)对数组进行划分.求以a 的函数形式表示的.最坏 ...

  4. Badusb制作,远程别人电脑

    Badusb制作 插一下U盘黑一台电脑,插了我的U盘你可就是我的脑了,(▽) 理论准备 我们要用它就应该知道他的工作原理是怎么样的,方便我们去发散思维去使用它. Badusb的原理是利用HID(Hum ...

  5. OpenStack-T版+Ceph

    OpenStack OpenStack 中有哪些组件 keystone:授权 [授权后各个组件才可以进行相应的功能] Keystone 认证所有 OpenStack 服务并对其进行授权.同时,它也是所 ...

  6. Codeforces 1254B1 - Send Boxes to Alice (Easy Version)

    题意 有\(n(1\leq n\leq 10^5)\)个盒子,每个盒子有\(a_i(0\leq a_i \leq 1)\)个糖果,你每一次可以将第\(i\)个盒子里的糖果放到第\(i-1\)或\(i+ ...

  7. Vue路由新开页面跳转和传参传递

    需求:在后台管理系统首页列表项中,点击详情跳转到系统中指定菜单的路由要求新开窗口并需要带上参数查询. 第一种方法: 1 const { id } = item; 2 let routeUrl = th ...

  8. 探索Lighthouse性能分数计算背后的奥秘

    作为开发我们都知道,页面性能很重要,一个性能良好的页面可以给用户带来非常好的用户体验.那么,怎么能知道自己写的页面性能是好是坏呢? Lighthouse 是Chrome提供给开发者用来测量页面性能的工 ...

  9. element ui的多个表格复选框,展开列显示错误

    今天在公司写页面的时候碰到一个bug,我们的那个页面上有多个表格. 用v-if来判断显示,然后再使用复选框和展开列的时候出了问题.先是复选框,第二个表格的复选框下一列不显示,我试了试,在下面的一列都会 ...

  10. 深入理解 Skywalking Agent

    概述 Agent 功能介绍 + 整体结构 + 设计 插件机制详解 Trace Segment Span 详解 异步 Trace 详解 如何正确地编写插件并防止内存泄漏 扩展:如何基于 Skywalki ...