Spring Cloud Nacos + @RefreshScope + @Value实现配置项动态刷新

一、基础实现

xml 复制代码
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
 </dependency>
java 复制代码
@Controller
@RequestMapping("/test")
@RefreshScope
public class TestController {

    @Value("${test.var}")
    String testVar;

    @RequestMapping("/fun")
    @ResponseBody
    public String fun1(){
        return testVar;
    }
}
  • application.yml
yaml 复制代码
# 开启Actuator的refresh端点
management:
  endpoints:
    web:
      exposure:
        include: refresh,configprops # 暴露刷新端点
  endpoint:
    refresh:
      enabled: true

以上的内容就可以实现@value注入配置项后动态加载,在nacos配置中心发布后,调用接口就可以读取新的值了

二、继续优化

java 复制代码
@Component
@RefreshScope
public class PropertiesHolder {

    @Value("${test.var}")
    private  String testVar;

    public String getTestVar() {
        return testVar;
    }
}
java 复制代码
@Controller
@RequestMapping("/test")
public class TestController {

    @Autowired
    PropertiesHolder propertiesHolder;

    @RequestMapping("/fun")
    @ResponseBody
    public String fun1(){
        return propertiesHolder.getTestVar();
    }
}

这样有个好处,就是更符合开闭原则和单一职责原则,

  • 配置项名字修改的时候只修改PropertiesHolder 类
  • 新增配置项只需要修改PropertiesHolder
  • 同一个配置项多个类里面引用,只需要PropertiesHolder 定义一次就够,不需要多个类中都写@Value()一样的内容

缺点就是用到的类都需要@Autowired PropertiesHolder不太友好

三、继续优化

java 复制代码
@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/fun")
    @ResponseBody
    public String fun1(){
        return PropertiesHolder.getTestVar();
    }
}
java 复制代码
@Component
@RefreshScope
public class PropertiesHolder implements InitializingBean {

    @Value("${test.var}")
    private String testVar;

    public static String getTestVar() {
        return INSTANCE.testVar;
    }

    private static PropertiesHolder INSTANCE = null;

    @Override
    public void afterPropertiesSet() throws Exception {
        INSTANCE = this;
    }
}

使用静态方法+单利(自身持有)的方式,提供静态方法将配置项暴漏出去。

从现在开始,nacos发布更新之后,程序里的配置内容不会更新了...

问题原因可以详细阅读一下Spring Cloud中@RefreshScope实现动态刷新的原理,总结起来就是原理是容器把@RefreshScope的Bean删除后再创建,所以此时拥有新配置项的PropertiesHolder 已经不是类静态变量INSTANCE 所引用的了,此时INSTANCE 的引用已经不归容器管理了,按理是会被回收的,解决思路就是监听刷新事件,发生刷新了就更新INSTANCE

解决办法就是继承RefreshEventListener 就可以了,这样在RefreshScope新建Bean时调用afterPropertiesSet来刷新类静态变量INSTANCE

java 复制代码
@Component
@RefreshScope
public class PropertiesHolder extends RefreshEventListener implements InitializingBean {

    @Value("${test.var}")
    private String testVar;

    public PropertiesHolder(ContextRefresher refresh) {
        super(refresh);
    }

    public static String getTestVar() {
        return INSTANCE.testVar;
    }

    private static PropertiesHolder INSTANCE = null;

    @Override
    public void afterPropertiesSet() throws Exception {
        INSTANCE = this;
    }
}
相关推荐
sww_10261 天前
RAG检索增强 ETL最佳实战
人工智能·python·spring
计算机学姐1 天前
基于SpringBoot的电影点评交流平台【协同过滤推荐算法+数据可视化统计】
java·vue.js·spring boot·spring·信息可视化·echarts·推荐算法
tb_first1 天前
万字超详细苍穹外卖学习笔记4
java·spring boot·笔记·学习·spring·mybatis
To Be Clean Coder2 天前
【Spring源码】createBean如何寻找构造器(四)——类型转换与匹配权重
java·后端·spring
笃行客从不躺平2 天前
Token 复习
java·分布式·spring cloud
键盘帽子2 天前
多线程情况下长连接中的session并发问题
java·开发语言·spring boot·spring·spring cloud
无名-CODING2 天前
Spring事务管理完全指南:从零到精通(上)
java·数据库·spring
多多*2 天前
2026年最新 测试开发工程师相关 Linux相关知识点
java·开发语言·javascript·算法·spring·java-ee·maven
树码小子2 天前
SpringIoC & DI (1):IOC介绍 & Spring IoC使用 & DI
java·后端·spring
tb_first2 天前
万字超详细苍穹外卖学习笔记5
java·数据库·spring boot·笔记·学习·spring