换掉你的@RefreshScope吧

在Spring Cloud中,@RefreshScope是用来动态刷新配置的,在和配置中心集成后,要想不重启动态刷新配置,需要在类上面加上@RefreshScope,但是这个注解因为其机制是销毁现有被标记对象重新创建新的被标记对象,存在一些问题,比如会将动态数据源的连接给干掉,导致mq的listener失效。为了解决这个问题,写了下面的小案例,可以用来替代这个注解。

项目地址:https://gitee.com/xuwenjingrencai/refresh-spring-cloud-starter

1、注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRefresh {

}

2、配置变更监听器以及动态刷新逻辑

@Component
@Slf4j
public class MyRefreshListener implements ApplicationRunner {

    private Map<String, List<BeanField>> map = new HashMap<>();

    @Autowired
    private Environment environment;

    @Autowired
    private ApplicationContext applicationContext;

    public void run(ApplicationArguments args) throws Exception {
        Map<String, Object> beansWithAnnotation =
                applicationContext.getBeansWithAnnotation(MyRefresh.class);
        for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
            String beanName = entry.getKey();
            Object bean = entry.getValue();

            Object target = AopTargetUtils.getTarget(bean);
            Class<?> targetClass = target.getClass();
            Field[] declaredFields = targetClass.getDeclaredFields();
            if (declaredFields.length <= 0) continue;
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(Value.class)) {
                    Value declaredAnnotation = field.getDeclaredAnnotation(Value.class);
                    String valueKey = getPropertyName(declaredAnnotation.value());
                    BeanField beanField = new BeanField();
                    beanField.setPropertyKey(valueKey);
                    beanField.setBeanName(beanName);
                    beanField.setFileName(field.getName());
                    beanField.setBean(bean);
                    beanField.setProxyObject(target);
                    if (map.containsKey(valueKey)) {
                        map.get(valueKey).add(beanField);
                    } else {
                        ArrayList<BeanField> beanFields = new ArrayList<>();
                        beanFields.add(beanField);
                        map.put(valueKey, beanFields);
                    }
                }
            }
        }
        log.info("refresh map 初始化完成");
    }

    @EventListener(EnvironmentChangeEvent.class)
    public void listener(EnvironmentChangeEvent event) throws Exception {
        for (String key : event.getKeys()) {
            String property = environment.getProperty(key);
            log.info("refresh key: {}", key);
            log.info("refresh value: {}", property);
            try {
                if (map.containsKey(key)) {
                    List<BeanField> beanFields = map.get(key);
                    for (BeanField beanField : beanFields) {
                        Object target = beanField.getProxyObject();
                        Field declaredField = target.getClass().getDeclaredField(beanField.getFileName());
                        declaredField.setAccessible(true);
                        declaredField.set(target, property);
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    private String getPropertyName(String propertyName) {
        return propertyName.substring(2, propertyName.length()-1);
    }
}
@Data
public class BeanField {
    private String propertyKey;
    private String beanName;
    private String fileName;
    private Object bean;
    private Object proxyObject;
}
public class AopTargetUtils {  

    public static Object getTarget(Object proxy) throws Exception {  

        if(!AopUtils.isAopProxy(proxy)) {  
            return proxy; //不是代理对象  
        }  

        if(AopUtils.isJdkDynamicProxy(proxy)) {  
            return getJdkDynamicProxyTargetObject(proxy);  
        } else { //cglib  
            return getCglibProxyTargetObject(proxy);  
        }  

    }  


    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {  
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");  
        h.setAccessible(true);  
        Object dynamicAdvisedInterceptor = h.get(proxy);  

        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");  
        advised.setAccessible(true);  

        Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();  

        return target;  
    }  

    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {  
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");  
        h.setAccessible(true);  
        AopProxy aopProxy = (AopProxy) h.get(proxy);  

        Field advised = aopProxy.getClass().getDeclaredField("advised");  
        advised.setAccessible(true);  

        Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();  

        return target;  
    }  

}

3、原理

当EnvironmentChangeEvent监听到配置变更事件时,将缓存中的与配置相关的对象取出并通过反射给对应的属性赋值

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值