日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

換掉你的@RefreshScope吧

作者:喜歡小蘋果的碼農(nóng) 更新時間: 2022-06-08 編程語言

在Spring Cloud中,@RefreshScope是用來動態(tài)刷新配置的,在和配置中心集成后,要想不重啟動態(tài)刷新配置,需要在類上面加上@RefreshScope,但是這個注解因?yàn)槠錂C(jī)制是銷毀現(xiàn)有被標(biāo)記對象重新創(chuàng)建新的被標(biāo)記對象,存在一些問題,比如會將動態(tài)數(shù)據(jù)源的連接給干掉,導(dǎo)致mq的listener失效。為了解決這個問題,寫了下面的小案例,可以用來替代這個注解。

項(xiàng)目地址:https://gitee.com/xuwenjingrencai/microservice

1、注解

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

}

2、配置變更監(jiān)聽器以及動態(tài)刷新邏輯

@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、原理

當(dāng)EnvironmentChangeEvent監(jiān)聽到配置變更事件時,將緩存中的與配置相關(guān)的對象取出并通過反射給對應(yīng)的屬性賦值

原文鏈接:https://blog.csdn.net/xuwenjingrenca/article/details/123929354

欄目分類
最近更新