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”來判斷.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM