一,背景

研究nacos时发现,springboot版本可使用@NacosValue实现配置的自动刷新,spring原生注解@Value则无法自动刷新

springcloud版本可采用两种方式实现自动刷新:

1.手动注入@NacosValue注解的处理器并使用该注解修饰相关字段或方法,这需要弄清楚底层的来龙去脉,比较麻烦,且不支持spring原生@Value注解。

2.借助@RefreshScope,将bean定义为RefreshScope。此方法也略显麻烦,每个存在配置需要刷新的类都要定义成RefreshScope。

本文为了解决上述问题而产生,实现@Value配置的自动刷新,

并且不是专门为nacos开发,只依赖springcloud的ContextRefresh机制(nacos的配置刷新也使用到了ContextRefresh)。

二,软件架构

基于springcloud的ContextRefresh机制,监听EnvironmentChangeEvent事件并重新注入@Value配置,需jdk1.8以上。

三,实现

package com.rutron.framework.conf;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;

/**
* 实现springcloud应用@Value配置的自动刷新
*
* @author lwc
*/
@Slf4j
@Configuration
@SuppressWarnings("ALL")
@ConditionalOnClass({ContextRefresher.class, RefreshScope.class, EnvironmentChangeEvent.class})
public class SpringValueAutoRefreshConfiguration {

public static final String PROCESSOR_BEAN_NAME = "com.rutron.framework.conf.SpringValueAutoRefreshProcessor";

@Role(ROLE_INFRASTRUCTURE)
@Bean(PROCESSOR_BEAN_NAME)
public BeanPostProcessor springValueAutoRefreshProcessor() {
return new SpringValueAutoRefreshProcessor();
}

}
package com.rutron.framework.conf;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
* 实现springcloud应用@Value配置的自动刷新
*
* @author lwc
*/
@Slf4j
@SuppressWarnings("ALL")
public class SpringValueAutoRefreshProcessor extends AutowiredAnnotationBeanPostProcessor implements BeanFactoryAware {

private ConfigurableListableBeanFactory beanFactory;

private final Class<? extends Annotation> refreshAnnotationType = Value.class;

private final Set<String> beanNamesNeedRefresh = new HashSet<>();

public SpringValueAutoRefreshProcessor() {
super.setAutowiredAnnotationType(this.refreshAnnotationType);
}

@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
//do nothing
}

@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
return pvs;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
this.processNeedRefresh(beanName, bean.getClass());
return bean;
}

@Override
public void setOrder(int order) {
super.setOrder(Ordered.LOWEST_PRECEDENCE - 1);
}

@EventListener(EnvironmentChangeEvent.class)
public void onApplicationEvent(EnvironmentChangeEvent event) {
final Set<String> keys = event.getKeys();
if (keys == null || keys.isEmpty()) {
return;
}
log.info("changed keys: {}", keys);
for (String beanName : beanNamesNeedRefresh) {
super.processInjection(beanFactory.getBean(beanName));
}
log.info("changed keys refresh finish");
}

private void processNeedRefresh(final String beanName, final Class<?> clazz) {
if (beanNamesNeedRefresh.contains(beanName)) {
return;
}
Class<?> targetClass = clazz;
do {
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (beanNamesNeedRefresh.contains(beanName) || Modifier.isStatic(field.getModifiers())) {
return;
}
AnnotationAttributes attributes = findAutowiredAnnotation(field);
if (Objects.nonNull(attributes)) {
beanNamesNeedRefresh.add(beanName);
}
});
if (!beanNamesNeedRefresh.isEmpty()) {
break;
}
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (beanNamesNeedRefresh.contains(beanName) ||
method.getParameterCount() == 0 || Modifier.isStatic(method.getModifiers())) {
return;
}
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes attributes = findAutowiredAnnotation(bridgedMethod);
if (Objects.nonNull(attributes) && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
beanNamesNeedRefresh.add(beanName);
}
});
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
}

private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
if (ao.getAnnotations().length > 0) {
return AnnotatedElementUtils.getMergedAnnotationAttributes(ao, refreshAnnotationType);
}
return null;
}
}