所有分类
  • 所有分类
  • 未分类

Spring Cloud-@RefreshScope动态刷新的注意事项

简介

本文介绍Spring Cloud的@RefreshScope动态刷新的注意事项。

不用@RefreshScope也能动态刷新

Spring Cloud的默认实现了动态刷新,不加@RefreshScope就能实现动态更新。方法如下:

法1:ApplicationContextHolder.getContext().getEnvironment().getRequiredProperty(key);

ApplicationContextHolder见:SpringBoot-静态获得Bean的工具类 – 自学精灵

法2:使用@ConfigurationProperties将配置放到类里边去

@ConfigurationProperties的用法见:SpringBoot-用类表示yml配置文件的值 – 自学精灵

原理

配置属性有ConfigurationPropertiesRebinder这个监听器,监听EnvironmentChangeEvent事件。当发生EnvironmentChange事件后,会刷新Environment,然后重新构造配置类对象。

@RefreshScope会失效

我们经常用@Value来使用配置,此时类上要加@RefreshScope。但是,@RefreshScope结合@Value这种用法,在一些场景下,自动刷新会失效!

会失效的场景

场景1:使用@RefreshScope的类必须是注入到容器中的bean,否则无效。

场景2:假如A组件注入B组件,B组件上使用了@RefreshScope并使用@Value获取配置,那么A组件上必须也加上@RefreshScope,否则无法实现动态刷新。

场景3:@RefreshScope 不能用在 @Scheduled、Listener、Timmer等类上,会有问题。以Scheduled为例:如果使用了@Scheduled的类里用@Value引入了配置,当配置中心修改了这个配置时,这个定时调度会失效(不再执行)。

解决方案

见:手写组件动态更新@Value的值 – 自学精灵

注意事项

  1. @RefreshScope作用的类,不能是final类,否则启动时会报错。
  2. 如果类里的方法有@XxlJob,不要使用@RefreshScope,否则定时任务报错:job handler [xxx] not found.(这个是XXL-JOB的问题,一直没解决)

静态变量用@RefreshScope的坑

代码

Controller

package com.knife.example.business.order.core.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class TestController {
    @Autowired
    private TestUtil testUtil;

    @GetMapping("test")
    public String test() {
        System.out.println(TestUtil.findConfig());
        testUtil.nothing();
        System.out.println(TestUtil.findConfig());
        return "success";
    }

}

工具类

package com.knife.example.business.order.core.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@Component
@RefreshScope
public class TestUtil {
    private static String name;

    @Value("${custom.name}")
    public void setName(String name) {
        TestUtil.name = name;
    }

    public static String findConfig() {
        return name;
    }

    public void nothing() {

    }
}

配置中心

测试

访问:http://localhost:9012/test

结果:

hello
hello

 修改配置中心的值,改为:

再次测试,访问:http://localhost:9012/test

结果

hello
hi

原因分析

动态刷新成功时,仅仅是再创建类一个代理对象,并清除了实际对象的缓存;再次通过代理对象来使用时,才会触发创建一个新的实例对象,此时才会更新配置的值。

0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录