配置的刷新,从第一篇就提出了这个问题,但是一直都没有说到,那么配置加载完毕之后能否在主动刷新呢?
如果对SpringCloud有了解的话,会直到有个配置中心的微服务,专门就是来做配置远程拉取,当然也支持刷新了,这是否意味着可以支持刷新呢,如果支持该怎么做?
I. 配置动态刷新
本篇将介绍并演示如何实现配置信息的刷新,但不会涉及到底层的实现原理,想要探究里面的神奇,可以网上google一下,或者期待后续的源码分析篇
1. ContextReferer
我们这里主要借助这个类来实现配置刷新,至于从哪里捞出来的这个东西,从Spring-Cloud-Config出发,看了下它怎么玩的,然后依葫芦画瓢
这个类全路径为 org.springframework.cloud.context.refresh.ContextRefresher
,因此你的SpringBoot项目需要做一点修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
|
接下来就简单了,直接调用这个类的refresh()
方法就可以了,just so easy~
2. 代码演示
配置文件: application.yml
1 2 3 4 5 6 7 8 9
| biz: refresh: ${random.long} key: refresh-test
rest: uuid: ${random.uuid}
server: port: 8081
|
读取配置的bean,演示了两种获取方式,分别如下
1 2 3 4 5 6 7
| @Data @Component @ConfigurationProperties(prefix = "biz") public class BizConfig { private String key; private Long refresh; }
|
开启刷新的@Value
注解方式,注意下面的@RefreshScoe
注解,这个必须有,负责更新后的配置不会同步
1 2 3 4 5 6 7
| @Data @RefreshScope @Component public class ValueConfig { @Value("${rest.uuid}") private String uuid; }
|
测试Controller
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @RestController public class DemoController { @Autowired private ContextRefresher contextRefresher;
@Autowired private BizConfig bizConfig;
@Autowired private ValueConfig valueConfig;
@GetMapping(path = "/show") public String show() { JSONObject res = new JSONObject(); res.put("biz", JSONObject.toJSONString(bizConfig)); res.put("uuid", valueConfig.getUuid()); return res.toJSONString(); }
@GetMapping(path = "/refresh") public String refresh() { new Thread(() -> contextRefresher.refresh()).start(); return show(); } }
|
3. 实例演示
启动上面的应用,然后开启愉快的测试,调用refresh接口,发现每次的返回都不一样(因为配置文件使用了random随机生成),但是访问show接口时,每次返回的都是一样的,也就是说refresh接口中确实实现了配置的刷新

说明
- 使用
ConfigurationProperties
方式获取注解时,自动支持刷新配置
- 使用
@Value
注解的方式,需要开启@RefreshScope
注解(上面没有演示不开启这个注解的情况, 建议有兴趣的可以自己尝试一下)
II. 配置变更监听
既然配置能刷新,那么如果我希望获取配置变更的事件,然后做一些其他的事情,是否ok呢?
其实进入 ContextRefresher
的源码,看下refresh接口,就很明确了
1 2 3 4 5 6 7 8 9 10 11
| public synchronized Set<String> refresh() { Map<String, Object> before = extract( this.context.getEnvironment().getPropertySources()); addConfigFilesToEnvironment(); Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); this.context.publishEvent(new EnvironmentChangeEvent(context, keys)); this.scope.refreshAll(); return keys; }
|
1. 配置变更监听
从上面的源码中,借助spring的事件通知机制,很简单就可以知道该怎么做了,来一个简单的demo,这里顺带测试下上面漏掉的不刷新的场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @RestController public class DemoController {
@Autowired private ContextRefresher contextRefresher;
@Autowired private BizConfig bizConfig;
@Autowired private ValueConfig valueConfig;
@Value("${rest.uuid}") private String uuid;
@GetMapping(path = "/show") public String show() { JSONObject res = new JSONObject(); res.put("biz", JSONObject.toJSONString(bizConfig)); res.put("uuid", valueConfig.getUuid()); res.put("no-refresh", uuid); return res.toJSONString(); }
@GetMapping(path = "/refresh") public String refresh() { new Thread(() -> contextRefresher.refresh()).start(); return show(); }
@EventListener public void envListener(EnvironmentChangeEvent event) { System.out.println("conf change: " + event); } }
|
直接将Listener写在Controller类内部… 原则上不推荐上面的写法
2. 实测
依然来个实测,主要注意下控制台的输出即可

III. 其他
0. 项目
1. 一灰灰Blog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
3. 扫描关注
一灰灰blog

知识星球

打赏
如果觉得我的文章对您有帮助,请随意打赏。
微信打赏
支付宝打赏