一,背景
研究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;
    }
}








