網(wǎng)站首頁 編程語言 正文
在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
相關(guān)推薦
- 2022-12-21 Docker教程之dockerfile構(gòu)建centos鏡像_docker
- 2022-08-01 如何利用python將Xmind用例轉(zhuǎn)為Excel用例_python
- 2022-03-12 linux下scp和sftp命令使用詳解_Linux
- 2022-12-12 Docker制作tomcat鏡像并部署項(xiàng)目_docker
- 2022-09-02 Redis?單機(jī)安裝和哨兵模式集群安裝的實(shí)現(xiàn)_Redis
- 2022-07-09 python沒有g(shù)pu,如何改用cpu跑代碼_python
- 2022-08-30 Token的生成、Token的作用及Base編碼的注意事項(xiàng)
- 2022-06-25 Android開發(fā)實(shí)現(xiàn)圖片大小與質(zhì)量壓縮及保存_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支