在之前的文章中我们分析了Nacos spring project的配置过程以及通用注解的实现,在这篇文章中将会针对Nacos spring project配置管理中提供的若干注解进行实现原理上面的分析。
@NacosValue
@Controller
@RequestMapping("config")
public class ConfigController {
@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)
private boolean useLocalCache;
@RequestMapping(value = "/get", method = GET)
@ResponseBody
public boolean get() {
return useLocalCache;
}
}
从官方文档的例子上我们可以得知@NacosValue注解在使用方法上与@Value注解相似,都可以利用占位符从配置文件中解析出对应的属性值,不同于@Value注解的是,@NacosValue提供了自动刷新的功能,即当远程的配置中心更新了属性值时能够自动将新值替换到类实例对应的属性上。
负责处理@NacosValue注解的NacosValueAnnotationBeanPostProcessor原理与AnnotationNacosInjectedBeanPostProcessor的原理相似,都是利用了AbstractAnnotationBeanPostProcessor来实现自定义注解的发现与注入,NacosValueAnnotationBeanPostProcessor在注入时首先会利用BeanFactory的resolveEmbeddedValue来解析对应的属性值(实际最后会与Environment关联),然后利用BeanFactory的TypeConverter将属性转换成属性对应的类型,最终由AnnotationNacosInjectedBeanPostProcessor利用反射注入到Bean实例对象中。
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean,
String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
//解析属性值
Object value = resolveStringValue(attributes.getString("value"));
Member member = injectedElement.getMember();
//转换并利用反射注入
if (member instanceof Field) {
return convertIfNecessary((Field) member, value);
}
if (member instanceof Method) {
return convertIfNecessary((Method) member, value);
}
return null;
}
不同之处有两点,第一点是在于postProcessBeforeInitialization回调函数中会扫描对应Bean中的属性与方法,获取所有带有@NacosValue注解的属性与方法,解析出注解上对应的属性占位符,封装成NacosValueTarget统一保存在placeholderNacosValueTargetMap中。第二点不同是NacosValueAnnotationBeanPostProcessor实现了ApplicationListener接口,用于监听NacosConfigReceivedEvent事件,前面提到过这些Nacos相关事件都是由ConfigService发出的,当本地应用接收到配置更新的事件后,会遍历整个placeholderNacosValueTargetMap,当原属性与新属性发生变更时,利用反射修改属性。
for (Map.Entry<String, List<NacosValueTarget>> entry : placeholderNacosValueTargetMap
.entrySet()) {
String key = environment.resolvePlaceholders(entry.getKey());
String newValue = environment.getProperty(key);
if (newValue == null) {
continue;
}
List<NacosValueTarget> beanPropertyList = entry.getValue();
for (NacosValueTarget target : beanPropertyList) {
String md5String = MD5Utils.md5Hex(newValue, <