Nacos Config客户端与Spring Boot、Spring Cloud深度集成
从源码角度,解析Nacos Config客户端与Spring Boot、Spring Cloud的深度集成
原创博文,转载请注明来源
Nacos与Spring Boot集成
@NacosPropertySource和@NacosValue
@PropertySource的用法并不陌生,它是spring原生的注解,我们可以这么用:
@Configuration
@PropertySource(value = "classpath:demo.properties",ignoreResourceNotFound = false)
public class SpringPropertysourceApplication {
//...
}
意思是:把在classpath路径下,名为demo.properties的配置文件注入到spring容器中,这样,我们就可以直接在类的属性上通过@Value注解获取到demo.properties属性值了。
Nacos为了达到以上目的,提供了一个叫@NacosPropertySource的注解,和@PropertySource目的一样:把配置注入到spring容器;使用方式一样,用于任意被spring管理的类上。当然,Nacos提供了更高级的功能,比如Property变更,自动刷新的功能,下面来分析一下,Nacos是怎么集成的
com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor
这个类实现了org.springframework.beans.factory.config.BeanFactoryPostProcessor(Spring钩子,它在所有spring bean定义生成后,实例化之前调用,允许覆盖或添加其属性)等接口,主要作用是,扫描由spring所有的bean,查看其类上,是否有@NacosPropertySource注解,如果有的话,则生成com.alibaba.nacos.spring.core.env.NacosPropertySource实例对象(@NacosPropertySource注解标注了当前PropertySource指定的DataId,也就是一个完成的配置文件,生成实例其实就是调用Nacos原生API获取配置构造NacosPropertySource对象),再把实例添加到spring env.PropertySources中去,其实完成这几步,我们就可以通过使用@Value或者ENV.getProperty()这种方式获取到由Nacos管理的配置项了。
NacosPropertySourcePostProcessor代码片段:
上面的功能仅仅是把Nacos的配置注入到spring中,那动态刷新的功能怎么做的呢。
回顾一下,原生的Nacos sdk是怎么样监听配置变化的
ConfigService configService = NacosFactory.createConfigService(properties);
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println("recieve:" + configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
可以看到,监听器与配置文件(DataId)是一对一的,所以,对于一个NacosPropertySource来说,应该有一个对应的监听器,在上诉NacosPropertySourcePostProcessor的代码片段截图中可以看到对应的代码:
这个添加listener的逻辑可以根据上面Nacos sdk的用法得出:
可以看到,在listener回调的逻辑里面,当有配置变更时会重新生成NacosPropertySource并替换掉ENV中过时的NacosPropertySource,完成这个部分的逻辑,我们通过ENV.getProperty()就可以动态获取到属性值了,但是通过@Value方式注入的到Bean对象的配置项,由于Bean已经生成,还是没办法动态更新,那Nacos是怎么做的?
com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor
上面说到如果通过ENV获取配置项的话,已经可以做到动态的目的了,但是如果此时持有配置项的Bean已经生成,则需要通过反射的机制,去动态更新了,从功能设计角度举个例子来讲清原理:
有类TestController,通过@Value的方式把demo.properties中app.config.threshold的配置项注入到属性threshold,那应该是这样的:
@RestController
@PropertySource(value = "classpath:demo.properties")
public class TestController {
@Value("${app.config.threshold}")
private String threshold;
}
那如果要通过反射设置属性的话那就需要这么一个映射关系:
app.config.threshold -> TestController.threshold
所以如果把这个映射关系保存在内存,当listener回调通知的时候,找到配置中的对应属性,反射设置进去就好了。
Nacos也是这么做的:
NacosValueAnnotationBeanPostProcessor实现了org.springframework.beans.factory.config.BeanPostProcessor(spring钩子函数,当bean对象实例化完成,注入容器之前调用 ),在其Object postProcessBeforeInitialization(Object bean, String beanName) 方法中,我们可以解析bean中所有注有@Value的注解,并将上诉映射关系,保存在内存中:
其中doWithFields实现如下:
其中有一点需要注意的地方,Nacos并没有使用原生的@Value注解去达到动态刷新的目的,因为违背了spring使用@Value的初衷,nacos自己实现了@NacosValue的注解
综上所诉,@NacosPropertySource和@NacosValue组合使用达到动态配置的效果是这样实现的:
@NacosValue
前面解析了@NacosPropertySource和@NacosValue组合使用达到动态配置原理,遗漏了一个细节点就是使用自定义注解@NacosValue是怎么在bean初始化的过程中注入属性的(前面说的动态刷新,是通过反射设置的,是建立在bean已经初始化完毕的基础上)。
还是com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor这个类,除了上面说的postProcessBeforeInitialization建立配置项和属性的映射关系这个方法外,还有两个方法,就是用于@NacosValue在bean初始过程中注入属性的:
简单理解一下这两个方法的目的:
doGetInjectedBean首先获取了@NacosValue中的配置项比如app.config.threshold,通过beanFactory解析出配置项对应的值(在ENV中),Member是一个队Field和Method的抽象类,如果Mem是Field则把取出的值进行转换和Field保持一致,如果是方法,则取出方法参数的Field进行转换
buildInjectedObjectCacheKey用于对doGetInjectedBean方法中已经转换过的值生一个cacheKey,这样就不用做多次转换的无用功
这两个方法都不是spring的钩子函数的方法,是在alibaba的spring-context-support包下,抽象类com.alibaba.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor提供的,这个类的作用是:解析被子类泛型指定的注解(public class NacosValueAnnotationBeanPostProcessor extends ValueAnnotationBeanPostProcessor)标记的属性或方法(抽象成了Member),并注入由getInjectedObject返回的值,这个方法只做了一层缓存(buildInjectedObjectCacheKey),并调用由子类扩展的方法doGetInjectedBean,完成注入
AnnotationInjectedBeanPostProcessor这个类的实现,参照了org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor的实现,而这个类就是用于Spring原生注解@Autowired 和@Value在bean初始化过程中注入依赖的。
Nacos Config客户端与Spring Boot、Spring Cloud深度集成的更多相关文章
- 新书上线:《Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统》,欢迎大家买回去垫椅子垫桌脚
新书上线 大家好,笔者的新书<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>已上线,此书内容充实.材质优良,乃家中必备垫桌脚 ...
- Java面试题(Spring Boot/Spring Cloud篇)
Spring Boot/Spring Cloud 104.什么是 spring boot? SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,所谓简化是指简化了Spring ...
- spring Boot+spring Cloud实现微服务详细教程第二篇
上一篇文章已经说明了一下,关于spring boot创建maven项目的简单步骤,相信很多熟悉Maven+Eclipse作为开发常用工具的朋友们都一目了然,这篇文章主要讲解一下,构建spring bo ...
- spring Boot+spring Cloud实现微服务详细教程第一篇
前些天项目组的大佬跟我聊,说项目组想从之前的架构上剥离出来公用的模块做微服务的开发,恰好去年的5/6月份在上家公司学习了国内开源的dubbo+zookeeper实现的微服务的架构.自己平时对微服务的设 ...
- Cola Cloud 基于 Spring Boot, Spring Cloud 构建微服务架构企业级开发平台
Cola Cloud 基于 Spring Boot, Spring Cloud 构建微服务架构企业级开发平台: https://gitee.com/leecho/cola-cloud
- spring boot、cloud v2.1.0.RELEASE 使用及技术整理
2018年10月30日 springboot v2.1.0.RELEASE 发布: https://github.com/spring-projects/spring-boot/releases/ta ...
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(七):集成 Druid 数据源
数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏 ...
- Spring Boot/Spring Cloud、ESB、Dubbo
如何使用Spring Boot/Spring Cloud 实现微服务应用spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现. ...
- 使用Spring Boot,Spring Cloud和Docker实现微服务架构
https://github.com/sqshq/PiggyMetrics Microservice Architecture with Spring Boot, Spring Cloud a ...
随机推荐
- 【ERP知识】一个VMI(供应商管理库存)实现方案
VMI,Vendor Managed Inventory,供应商管理库存 是指客户不采购或尽量少采购物料,而是由供应商保证该物料有充足的数量,在客户需要的时候能按时提供. 这样可以降低客户方的库存成本 ...
- 重新认识new
前言 感谢大佬:https://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html www.cplusplus.com 因为这段时间在重 ...
- jenkins构建后操作archive the artfacts的用法
参考: https://blog.csdn.net/liqiangeastsun/article/details/79062806 Jenkins构建完成存档 Archive the artifact ...
- mac 上查看python3的各种安装路径
1.mac chromedriver的安装目录:/usr/local/bin 2.mac htmltestrunner的存放目录:命令行下 import sys sys.path/Library/Fr ...
- appium常见问题05_修改Android手机运行环境(adb指令修改hosts)
自动化测试过程中,手机有时会跳网,怎样保持手机测试的环境稳定性,可以通过adb指令修改android手机hosts,保持手机运行在hosts中配置的环境中: 修改方法如下: 前提条件:已安装andro ...
- css浮动以及清除
首先要知道,div是块级元素,在页面中独占一行,自上而下排列,也就是传说中的流.如下图: 可以看出,即使div1的宽度很小,页面中一行可以容下div1和div2,div2也不会排在div1后边,因为d ...
- python-javascript之dom
DOM DOM:(document object mode)文档对象模型.DOM为文档提供了结构化表示,并定义了如何通过脚本来访问文档结构. 目的就是为了能让js操作html元素而制定的一个规范 DO ...
- [LeetCode] 627.交换性别
给定一个 salary 表,如下所示,有 m = 男性 和 f = 女性 的值.交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然). 要求只使用一个更新(Update)语句,并且没 ...
- c# .netframwork 4.0 调用 2.0时报错 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。
“System.IO.FileLoadException”类型的未经处理的异常在 XXX.dll 中发生 其他信息: 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的 ...
- Yii 1.1 cookie删不掉
我的cookie是这样设置的: $cookie = new CHttpCookie('username','Jack'); $cookie->expire = time()+60*60*24*3 ...