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下派生子類。
config-server中@RefreshScope的"陷阱"