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;
    }
}
相关推荐
一定要AK8 分钟前
Spring 入门核心笔记
java·笔记·spring
凯尔萨厮25 分钟前
创建SpringWeb项目(Spring2.0)
spring·mvc·mybatis
木子欢儿2 小时前
Docker Hub 镜像发布指南
java·spring cloud·docker·容器·eureka
Devin~Y2 小时前
高并发电商与AI智能客服场景下的Java面试实战:从Spring Boot到RAG与向量数据库落地
java·spring boot·redis·elasticsearch·spring cloud·kafka·rag
鬼先生_sir3 小时前
Spring Cloud 微服务监控实战:SkyWalking + Prometheus+Grafana 全栈解决方案
运维·spring cloud·grafana·prometheus·skywalking
凯尔萨厮6 小时前
Spring学习笔记(基于配置文件)
spring
bobasyu6 小时前
Claude Code 源码笔记 -- queryLoop
java·笔记·spring
计算机学姐6 小时前
基于SpringBoot的高校竞赛管理系统
java·spring boot·后端·spring·信息可视化·tomcat·mybatis
文慧的科技江湖6 小时前
光储充协同的终极闭环:用SpringCloud微服务打造“发-储-充-用“智能能源网络 - 慧知开源充电桩管理平台
java·开发语言·spring cloud·微服务·能源·充电桩开源平台·慧知重卡开源充电桩平台
砍材农夫7 小时前
spring-ai 第七模型介绍-向量模型
java·人工智能·spring