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;
    }
}
相关推荐
paopaokaka_luck35 分钟前
智慧社区综合服务小程序(人脸识别、AI问答、Echarts图形化分析)
前端·javascript·spring boot·spring·数据分析·echarts
牧瀬クリスだ40 分钟前
YAML配置详解:SpringBoot实战指南
java·spring·yml
需要8261 小时前
JVM 内存区域与对象创建:一次 GC 从哪来
java·jvm·spring boot·spring·servlet·tomcat
paopaokaka_luck4 小时前
考研政治刷题小程序(AI推荐题目、ECharts数据分析、章节练习与专项训练、模拟考试、错题本与收藏夹、学习打卡与目标管理、题库和组卷维护)
前端·javascript·spring boot·spring·数据分析·echarts
步行cgn5 小时前
Spring 基于 XML 的自动装配:byType 详解
java·后端·spring
边境悍匪5 小时前
蜗牛学苑 Java 智能体学习 Day46|贯穿项目 2 思维导图复盘
java·开发语言·vue.js·学习·spring
步行cgn13 小时前
Spring c 命名空间注入详解
java·后端·spring
Rain的Java大神之路15 小时前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
需要82618 小时前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
vx-程序开发19 小时前
【计算机毕设】django校园跑腿服务系统83141
java·数据库·vue.js·spring boot·spring·elasticsearch·django