spring cloud:config-server中@RefreshScope的"陷阱"

举报
菩提树下的杨过 发表于 2019/01/29 22:37:17 2019/01/29
【摘要】 spring cloud的config-serfver主要用于提供分布式的配置管理,其中有一个重要的注解:@RefreshScope,如果代码中需要动态刷新配置,在需要的类上加上该注解就行。但某些复杂的注入场景下,这个注解使用不当,配置可能仍然不动态刷新,比如下面的场景:1. 先定义一个配置类(假设这里面定义了一个apiUrl,表示调用的api地址)@Component@Configurat...

spring cloud的config-serfver主要用于提供分布式的配置管理,其中有一个重要的注解:@RefreshScope,如果代码中需要动态刷新配置,在需要的类上加上该注解就行。但某些复杂的注入场景下,这个注解使用不当,配置可能仍然不动态刷新,比如下面的场景:

1. 先定义一个配置类(假设这里面定义了一个apiUrl,表示调用的api地址)

@Component
@ConfigurationProperties(prefix = "demo.app")
@Data
@RefreshScope
public class DemoServiceAppConfig  {
    /**
     * api调用地址
     */
    private String apiUrl = "";
}

 对应的yml配置类似:

1
2
3
demo:
  app:
    apiUrl: "http://11111.com/xxxxx"

 

2. 然后定义一个工具类,用于封装调用外部api

1
2
3
4
5
6
7
8
9
10
@Data
@RefreshScope
public class TestUtil {
 
    private String apiUrl;
 
    public void callApi() {
        System.out.println("apiUrl:" + apiUrl);
    }
}

 

3. 为了避免1中的配置类,与2中的工具类强耦合,搞一个bean注入容器把他们关联起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
@RefreshScope
public class BeanContainer {
 
    @Autowired
    DemoServiceAppConfig appConfig;
 
    @Bean
    private TestUtil testUtil() {
        TestUtil testUtil = new TestUtil();
        testUtil.setApiUrl(appConfig.getApiUrl());
        return testUtil;
    }
 
}

 

4 最后来一个Controller测试下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RefreshScope
@Api(consumes = "application/json",
        produces = "application/json",
        protocols = "http",
        basePath = "/")
public class PingController extends AbstractController {
 
    @Autowired
    DemoServiceAppConfig appConfig;
 
    @Autowired
    TestUtil testUtil;
 
    @RequestMapping(value = "/test", method = {RequestMethod.GET, RequestMethod.POST})
    public String test() {
        return "config.apiUrl=>" + appConfig.getApiUrl() + "<br/>testUtil.apiUrl=>" + testUtil.getApiUrl();
    }
 
}  

注:上面所有这些类,都加了@RefreshScope标签

 

跑起来,效果如下:

然后把yml文件改下,然后push到git上,再curl -X POST http://localhost:7031/refresh 刷一把配置

可以看到,通过testUtil调用的方法中,取到的apiUrl值仍然是旧的,并没有动态刷新

正确姿势如下:

01.png

最后一个问题,@RefreshScope作用的类,不能是final类,否则启动时会报错,类似下面这堆:

Caused by: java.lang.IllegalArgumentException: Cannot subclass final class TestUtil
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:565) ~[spring-core-4.3.9.RELEASE.jar:4.3.9.RELEASE]

从出错信息上看,底层应该是使用cglib进行增强,需要在TestUtil下派生子类。 

然后,由cglib又引出了更一个坑,如果在一些web核心组件相关的config上误加了@RefreshScope, 比如下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Bean
@RefreshScope
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

这里面有一个org.springframework.web.cors.CorsConfiguration配置类,加了@RefreshScope后,org.springframework.web.filter.GenericFilterBean#init 这个核心bean的init就会报错,要么应用启不起来,要么请求时报内部错误。

 

最后,还有一个要注意的坑,比如:

abc: "xxx"

如果yml文件中有一个这样的属性,改成:

abc: ""

即变成空后,就算再curl -X POST  http://.../refresh 接口,代码中拿到的值,仍然是xxx,建议如果要让一个属性值失效,可以约定一个特定值,比如

abc:"NULL"

然后代码中用“NULL”来判断.


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200