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;
    }
}
相关推荐
CoderYanger1 小时前
Java EE 进阶:5. Spring IoC&DI(第三弹):Spring DI
java·开发语言·spring boot·spring·面试·java-ee·mvc
郭小李1 小时前
spring中@Transactional注解对事务的支持
数据库·spring·oracle
shuair1 小时前
Spring Cloud 微服务脚手架:Simple Cloud 系统架构设计
spring cloud·微服务·系统架构
步行cgn14 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
霸道流氓气质16 小时前
智能客服组件:分层意图识别、RAG Advisor 与人工转接状态机的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
captain37616 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
步行cgn17 小时前
Spring MVC + Thymeleaf 模板解析失败:从 500 错误到页面正常显示
java·spring·mvc
学到老活到老18 小时前
第 24 集:配置文件写了 80,Spring Boot 为什么读到 90?
java·后端·spring
Flynn Liang18 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
二哈赛车手20 小时前
新人笔记---hermes定时功能设计,具体讲解以及可优化点
java·人工智能·笔记·spring·ai·typescript