Apollo 2 如何支持 @Value 注解自动更新
前言
Apollo 在 v0.10.0 版本后,支持自动更新。v0.10.0之前的版本在配置变化后不会重新注入,需要重启才会更新。
也就是说,如果一个属性加入了 @Value 注解,并且这个配置在配置中心也存在,那么,配置中心修改属性值后,就会自动更新这个值。同时,有个开关可以控制这个功能是否关闭(默认开启)。
配置文件中写入 apollo.autoUpdateInjectedSpringProperties = false 即可关闭该功能。
如何实现?
此次提交的 PR 详情可见 https://codecov.io/gh/ctripcorp/apollo/pull/972/diff.
大致先说下实现思路:
Apollo 实现了BeanPostProcessor 接口,这个接口的作用则是每个 bean 初始化成前后做操作。
在 postProcessBeforeInitialization 方法中,会取出这个 Bean 的所有属性和方法,并判断他们是否含有 @Value 注解从而进行处理。
具体处理逻辑,则是: 将符合条件的属性封装成一个 SpringValue 对象,放在一个Map 中。当 clien 检测到配置发生变化时,就会更新这个 Map 里面的值,从而达到自动更新的目的。
当然,这只是大概的思路,具体细节则要复杂一些。接下来我们就说说具体的实现细节。
实现细节
相关类:
SpringValue@Value 注解的详细信息数据结构。SpringValueRegistry@Value 注册中心,保存了他的 key/value 机构。SpringValueDefinitionProcessor针对 Spring 3.x 版本做的特殊操作。SpringValueProcessor处理 @Value 注解的类。ConfigPropertySourcesProcessor注册 SpringValueProcessor 到容器。PropertySourcesProcessor将 Config 和自动更新监听器绑定,同时注入 Spring 环境。
逻辑步骤:
无论是 XML 方式,注解方式,SpringBoot 方式,都会触发注册机制,即自动注册
SpringValueProcessor处理器到 Spring 容器(他主要是个BeanPostProcessor)。这是 apollo 自定义的@Value处理器。不仅仅注册
SpringValueProcessor,还注册PropertySourcesProcessor,这是一个BeanFactoryPostProcessor,即在 Spring bean 工厂初始化后,可以进行修改的一个类。这里的时机比SpringValueProcessor早。PropertySourcesProcessor在具体方法中,会初始化所有的 Config(ConfigService.getConfig(namespace)),并设置到 Spring 的环境中(存储所有的 Property,且同名状态下优先级最高,目的是让 Spring 自己注入到变量中)。同时,创建一个自动更新监听器,监听所有的 Config。SpringValueProcessor在容器初始化 Bean 的时候,会处理所有带有@Value注解的类,并放入到SpringValueRegistry的 Map 中。注意:SpringValueRegistry是单例的。而 自动更新监听器 也是包含一个SpringValueRegistry的。因此,每当一个 Config 变化的时候,都会触发 change 事件,并调用监听器的 onChange 方法,如果匹配,该方法则会更新SpringValueRegistry内部的值——完成自动更新。这里有个问题:所有的配置都放在
SpringValueRegistry中,一个 client 会持有多个namespace,每个namespace可能会有重名的配置。那么会不会发生冲突呢?实际上,apollo 考虑到了这点,在设计namespace的时候,就有一个 order 属性,用于处理这种情况,order 越小,优先级越高。当一个配置没有显时的设置namespace时,apollo 将其归纳为优先级最高的namespace——即 order 最小的namespace。而实现优先级的则是Spring PropertySource内部的排序机制,说白了就是一个 List,优先级最高的下标为0,当循环匹配的时候,优先匹配下标为0的配置。在
shouldTriggerAutoUpdate方法里,有个判断很绕:根据 key 获取 Spring 环境中的配置值,判断这个值和刚刚发生的变化值是否相等,如果相等,就更新 value,反之,跳过此次事件。
解释一下:我们知道,@value对应的是优先级最高的namespace,environment获取的也是优先级最高的 namespace 的配置。
如果一个配置更新了,但environment优先级最高的配置却没有更新,那么@value对应的更新事件就不应该触发。
如果一个配置更新了,environment获取到的优先级最高的配置也更新了,那么@value对应的更新时间就应该触发。
这里,其实就是@value是否更新的重要判断。
总结
关于这个小功能,为什么要单独拉出来说一说呢?实际上,该功能涉及到的东西还是很多的。例如:
- 一个普通的 Java 项目如何和 Spring 框架融合 —— 注册,初始化,注入 Spring 环境等操作。
- 如何玩转 Spring 的
PropertySource的 order 机制。 - 每一个
namespace都有一个长轮询,发生更新后,apollo 触发监听器的更新事件,其中包括自动更新监听器,但是,需要通过shouldTriggerAutoUpdate的判断才能进行更新,因为 @value 可能会和多个 namespace 重名,需要通过优先级来过滤。即:如果 Spring 环境中优先级最高的 config 更新了,那么@value对应的 field 就需要更新,反之,不能更新(namespace不匹配)。
Apollo 2 如何支持 @Value 注解自动更新的更多相关文章
- NEO4J中文分词全文索引自动更新解决方案
NEO4J中文分词全文索引自动更新解决方案 一.样例数据 二.英文与中文全文索引差别 1.创建NEO4J默认索引 2.删除索引 3.创建支持中文分词的索引 三.APOC自带英文全文索引过程(可自动更新 ...
- CS程序自动更新实现原理及代码(支持多版本多文件更新)
公司主要项目为CS端,经常遇到客户需求变更及bug处理,在没有引用自动更新之前每次更新程序,必须手动对每个客户端进行更新,这样导致技术支持工作量特别大,也给客户不好的印象,因此我需要一个自动更新程序! ...
- eclipes创建一个web项目web.xml不能自动更新的原因(web.xml和@WebServlet的作用)
在eclipse中创建一个Web项目的时候,虽然有web.xml生成,但是再添加Servlet类文件的时候总是看不见web.xml的更新,所以异常的郁闷!上网查了查,原来我们在创建Web项目的时候,会 ...
- spring cloud学习(六) 配置中心-自动更新
上一篇学习了spring cloud config的基本使用,但发现有个问题,就是每次更改配置后,都需要重启服务才能更新配置,这样肯定是不行的.在上网查资料了解后,spring cloud支持通过AM ...
- python自动更新pom文件
前言 项目越来越多,版本管理越来越麻烦,在项目上我使用 maven version 来进行版本管理.主要还是在分布式项目中模块众多的场景中使用,毕竟各个模块对外的版本需要保持统一. 关于这个插件如何使 ...
- 解析大型.NET ERP系统 自动更新
C/S架构的应用程序需要支持自动更新功能,当新版本程序发布后,正在运行的客户端能检测到新版本的程序,通知用户是否下载更新.工作以来参与过几个自动更新模块的设计与维护,撰文总结自动更新模块设计与实现. ...
- Atitit 热更新资源管理器 自动更新管理器 功能设计
Atitit 热更新资源管理器 自动更新管理器 功能设计 · 多线程并行下载支持 · 两层进度统计信息:文件级以及字节级 · Zip压缩文件支持 · 断点续传 · 详细的错误报告 · 文件下载失败重试 ...
- [mysql] timestamp自动更新和初始化
1.概述 在我们设计表的时候,考虑将行数据的创建时间和最后更新时间记录下来是很好的实践.尤其是可能需要做数据同步或者对数据新鲜度有要求的表.举些应用场景,更新距上次更新超过2小时的行数据,或者是将一个 ...
- 使用友盟进行apk的自动更新
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
随机推荐
- 位运算------按位与、按位或、按位异或、取反、<<、>>、>>>
程序中的所有数在计算机内存中都是以二进制的形式储存的,位运算就是直接对整数在内存中的二进制位进行操作. 知识点: 1.原码.反码.补码(以byte的1.-1举例) 示例 ...
- drf6 权限和频率控制组件
对某件事情决策的范围和程度,我们叫做权限,权限是我们在项目开发中非常常用到的. DRF框架给我们提供的权限组件 权限组件 之前DRF的版本和认证,知道了权限和频率跟版本认证都是在initial方法里初 ...
- linux上安装zookeeper
本文先讲述Linux下单机版的安装流程,集群的配置后续再补上.关于Zookeeper的基本介绍和原来在本文不做更多介绍,可以自行查找.本文的操作流程相对简单,仅做备忘而已. 第一步 安装JDK: ...
- 获取Lambda表达式内表达式的值
随着Linq的盛行,对于Linq和Lmabda表达式的使用也越来越多,Lambda表达式在.net framework 3.5中提出来,Lambda表达式是一个匿名方法,通常在LINQ中被用来创建委托 ...
- android-glsurfaceview Activity框架程序
两个基本的类让我们使用OpenGL ES API来创建和操纵图形:GLSurfaceView和 GLSurfaceView.Renderer. 1. GLSurfaceView: 这是一个视图类,你可 ...
- 移植U-Boot时遇到的问题
1. 在lowlevel_init过程中,本来想实现一个串口直接打印字符串的过程,编译的时候出现了问题,说是发现代码执行段没有对齐:“unaligned opcodes detected in exe ...
- Visual C++.NET设计
首先,创建对应的工程,VS2010以及以前的版本,创建项目时都可以在CLR下找到“Windows窗体应用程序”的项目模板,但是VS2012以后的版本就没这么方便了.可以通过打开旧版本的项目来修改,也可 ...
- python实战提升--1
#python实战提升 1. 如何在列表.字典.集合中根据条件筛选数据? python中for _ in range(10)与for i in range(10)有何区别 下划线表示 临时变量, 仅用 ...
- utf-8转gb2312
近日在对一个json串进行转码时,显示中文乱码,原因是json串编码方式为utf-8,而我程序在windows上采用的是多字节编码方式,即采用gb2312编码.这里就存在一个utf-8到gb2312的 ...
- Visual Studio 2017 调试器的工作进程(msvsmon.exe)意外退出 调试将终止
开发环境: Windows 10 in Parallels Desktop Visual Studio 15.6 场景还原: 使用 Visual Studio 15.6 (即 Visual Studi ...