前言
紧接上文,nacos-spring-boot版本不支持@Value注解的属性的动态更改,那么我们可以依赖nacos提供的一些扩展机制,自己来实现这个功能
实现
直接利用nacos配置更改时会发布的事件NacosConfigReceivedEvent,扩展一个监听该事件的监听器即可
package com.alibaba.nacos.example.spring.boot.controller;
import com.alibaba.nacos.spring.context.event.config.NacosConfigReceivedEvent;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.*;
/**
* @value注解动态更新监听器
*/
@Component
public class ValueAnnotationApplicationListener implements BeanFactoryAware,
ApplicationListener<NacosConfigReceivedEvent> {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
/**
* 判断字段是否有@Value注解
*
* @param ao
* @return
*/
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
MergedAnnotation<?> annotation = annotations.get(Value.class);
if (annotation.isPresent()) {
return annotation;
}
return null;
}
@Override
public void onApplicationEvent(NacosConfigReceivedEvent event) {
Iterator<String> beanNamesIterator = beanFactory.getBeanNamesIterator();
// 循环所有的bean
while (beanNamesIterator.hasNext()) {
String beanName = beanNamesIterator.next();
Object bean = beanFactory.getBean(beanName);
// 取出类本身的属性成员(包括私有、共有、保护)
Field[] allFields = bean.getClass().getDeclaredFields();
for (Field field : allFields) {
// 如果有@Value注解
MergedAnnotation mergedAnnotation = findAutowiredAnnotation(field);
if (mergedAnnotation != null) {
// 以下代码拷贝自AutowiredAnnotationBeanPostProcessor类
DependencyDescriptor desc = new DependencyDescriptor(field, true);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
// 解析出value值
Object value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
ReflectionUtils.makeAccessible(field);
try {
Object oldValue = field.get(bean);
if (!Objects.equals(oldValue, value)) {
field.set(bean, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
让该类可以被spring扫描到即可,以上代码的来源是spring本身对@Value注解的实现类AutowiredAnnotationBeanPostProcessor,同时利用了它内部解析${xxx}的解析器
大概的思路
- 就是扫描所有的bean
- 判断那些bean里面有需要修改的字段
- 重新把这里字段去spring的environment中查询
- 如果和当前不一样,那么重新赋值即可
以上代码仅供参考,还有需要优化的地方,比如
- 扫描所有bean会不会比较耗时,影响性能
- 对方法上的@Value的处理
总结
以上功能可以简单的实现,实现思路比较明确,也可以看下apollo动态配置中心是如何实现这个功能的,可以参考下怎么写比较优雅