Nacos Config客户端与Spring Boot、Spring Cloud深度集成
啥也不懂的新同学 人气:1目录
- Nacos与Spring Boot集成
- @NacosPropertySource和@NacosValue
- com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor
- com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor
- @NacosValue
- @NacosPropertySource和@NacosValue
从源码角度,解析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
AnnotationInjectedBeanPostProcessor这个类的实现,参照了org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor的实现,而这个类就是用于Spring原生注解@Autowired 和@Value在bean初始化过程中注入依赖的。
加载全部内容