zoukankan      html  css  js  c++  java
  • spring cloud:config-server中@RefreshScope的"陷阱"

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

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

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

     对应的yml配置类似:

    demo:
      app:
        apiUrl: "http://11111.com/xxxxx"
    

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

    @Data
    @RefreshScope
    public class TestUtil {
    
        private String apiUrl;
    
        public void callApi() {
            System.out.println("apiUrl:" + apiUrl);
        }
    }
    

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

    @Component
    @RefreshScope
    public class BeanContainer {
    
        @Autowired
        DemoServiceAppConfig appConfig;
    
        @Bean
        private TestUtil testUtil() {
            TestUtil testUtil = new TestUtil();
            testUtil.setApiUrl(appConfig.getApiUrl());
            return testUtil;
        }
    
    }
    

    4 最后来一个Controller测试下

    @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值仍然是旧的,并没有动态刷新

    正确姿势如下:

    最后一个问题,@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, 比如下面这样:

        @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”来判断.

  • 相关阅读:
    Appium 服务命令行参数
    DC 输入 输出 时钟 PVT设置
    .synopsys_dc.setup编写
    Excel VBA编程常用语句300句
    C# 泛型单例工厂
    C# Winform与JS交互
    SQL分析“聚集索引、非聚集索引”区别
    C# ClassHelper动态创建程序集和类, 添加/删除类属性
    从30个角度对比 PostgreSQL 和 MySQL
    C# 常用类和命名空间
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/8085530.html
Copyright © 2011-2022 走看看