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;
    }
}
相关推荐
谢亮_vipxieliang3 小时前
ValidX分组验证详解:不同场景使用不同验证规则
java·spring boot·后端·spring cloud·eclipse·hibernate
凤山老林4 小时前
Spring Boot 集成 Spring Vault:集中式密钥管理与动态凭证轮换
spring boot·后端·spring·vault·集中式秘钥管理
catino5 小时前
spring-Bean
java·后端·spring
AIGC小尼7 小时前
2026 最新实战|SpringCloud 微服务整合 SpringAI 完整落地教程(RAG 知识库 + 限流降级 + 跨服务调用)
spring·spring cloud·微服务
2601_962055818 小时前
Spring全部注解
java·后端·spring
pnoker10 小时前
拆解 IoT DC3:六层微服务架构
java·物联网·spring cloud·微服务·架构
Supersist11 小时前
【Spring】 BeanFactory 和 ApplicationContext 体系结构
后端·spring
Sam_Deep_Thinking12 小时前
聊聊开闭原则,以及它在Spring里的样子
java·后端·spring·程序员·开闭原则
SQL-First布道者13 小时前
⚡ Spring JDBC 完整体系 · 第 5 讲 · Spring Boot Starter JDBC
java·spring boot·spring·mybatis·spring jdbc
2601_9620763313 小时前
Spring Cloud 负载均衡器架构选型
spring cloud·架构·负载均衡