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”來判斷.
作者:
菩提樹下的楊過出處:
http://yjmyzz.cnblogs.com本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。