一、基础实现
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;
}
}