前言
最近在寫框架時(shí)遇到需要根據(jù)特定配置(可能不存在)加載 bean 的需求,所以就學(xué)習(xí)了下 Spring 中如何獲取配置的幾種方式。
Spring 中獲取配置的三種方式
- 通過 @Value 方式動(dòng)態(tài)獲取單個(gè)配置
- 通過 @ConfigurationProperties + 前綴方式批量獲取配置
- 通過 Environment 動(dòng)態(tài)獲取單個(gè)配置
通過 @Value 動(dòng)態(tài)獲取單個(gè)配置
-
作用
- 可修飾到任一變量獲取,使用較靈活
-
優(yōu)點(diǎn)
- 使用簡(jiǎn)單,且使用關(guān)聯(lián)的鏈路較短
-
缺點(diǎn)
配置名不能被有效枚舉到
每一個(gè)配置的使用都需重新定義,使用較為麻煩
項(xiàng)目強(qiáng)依賴配置的定義,配置不存在則會(huì)導(dǎo)致項(xiàng)目無法啟動(dòng)
-
使用場(chǎng)景
項(xiàng)目強(qiáng)依賴該配置的加載,想要從源頭避免因配置缺失導(dǎo)致的未知問題
只想使用少數(shù)幾個(gè)配置
-
代碼示例
@Configuration public class ConfigByValueAnnotation { @Value("${server.port}") private String serverPort; public String getServerPort() { return serverPort; } } 測(cè)試代碼
@DisplayName("multipart get config")
@SpringBootTest
public class MultipartGetConfigTest {
private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class);
@Autowired
private ConfigByValueAnnotation configByValueAnnotation;
@Test
public void getByValueAnnotation(){
log.info("get by @Value, value: {}", configByValueAnnotation.getServerPort());
}
}
- 測(cè)試結(jié)果
org.spring.demo.MultipartGetConfigTest : get by @Value, value: 7100
通過 @ConfigurationProperties + 前綴方式批量獲取
-
作用
- 用于配置類的修飾或批量配置的獲取
-
優(yōu)點(diǎn)
使用配置只需確定 key 的前綴即能使用,有利于批量獲取場(chǎng)景的使用
因采用前綴匹配,所以在使用新的相同前綴 key 的配置時(shí)無需改動(dòng)代碼
-
缺點(diǎn)
使用復(fù)雜,需定義配置類或者手動(dòng)創(chuàng)建 bean 后引入使用
增加新的前綴相同 key 時(shí)可能會(huì)引入不穩(wěn)定因素
-
使用場(chǎng)景
需要同時(shí)使用多前綴相同 key 的配置
期望增加新配置但不修改代碼的 properties 注入
-
代碼示例
@Component @ConfigurationProperties(prefix = "server", ignoreInvalidFields = true) public class ConfigByConfigurationProperties { private Integer port; public Integer getPort() { return port; } public ConfigByConfigurationProperties setPort(Integer port) { this.port = port; return this; } }@Configuration public class ConfigByConfigurationPropertiesV2 { @Bean("configByValueAnnotationV2") @ConfigurationProperties(prefix = "server2") public Properties properties(){ return new Properties(); } }- 測(cè)試代碼
@DisplayName("multipart get config")
@SpringBootTest
public class MultipartGetConfigTest {
private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class);
@Autowired
private ConfigByConfigurationProperties configByConfigurationProperties;
@Autowired
@Qualifier("configByValueAnnotationV2")
private Properties properties;
@Test
public void getByConfigurationProperties(){
log.info("get by @ConfigurationProperties, value: {}", configByConfigurationProperties.getPort());
log.info("get by @ConfigurationProperties and manual create bean, value: {}", properties.getProperty("port"));
}
}
- 測(cè)試結(jié)果
org.spring.demo.MultipartGetConfigTest : get by @ConfigurationProperties, value: 7100
org.spring.demo.MultipartGetConfigTest : get by @ConfigurationProperties and manual create bean, value: 7100
通過 Environment 動(dòng)態(tài)獲取單個(gè)配置
-
作用
- 用于動(dòng)態(tài)在程序代碼中獲取配置,而配置 key 不需提前定義
-
優(yōu)點(diǎn)
獲取的配置的 key 可不提前定義,程序靈活性高
配置 key 可使用枚舉統(tǒng)一放置與管理
-
缺點(diǎn)
使用較復(fù)雜,需繼承 Environment 接口形成工具類進(jìn)行獲取
獲取 key 對(duì)應(yīng)的枚舉與 key 定義分離,value 獲取鏈路較長(zhǎng)
-
使用場(chǎng)景
只需使用少量的配置
獲取配置的 key 無提前定義,需要根據(jù)對(duì)配置的有無進(jìn)行靈活使用
代碼示例
@Component
public class ConfigByEnvironment implements EnvironmentAware {
private static final Logger log = LoggerFactory.getLogger(ConfigByEnvironment.class);
private Environment environment;
public Optional<String> get(String configKey){
String config = environment.getProperty(configKey);
return Objects.isNull(config) ? Optional.empty() : Optional.of(config);
}
public void get(String configKey, Consumer<String> consumer){
Optional<String> config = get(configKey);
if(!config.isPresent()){
log.warn("application config, get config by key fail, key: {}", configKey);
}
config.ifPresent(consumer);
}
@Override
public void setEnvironment(@NonNull Environment environment) {
this.environment = environment;
}
}
public enum ConfigByEnvironmentKey {
SERVER_PORT("server.port", "server port");
private String key;
private String description;
ConfigByEnvironmentKey(String key, String description) {
this.key = key;
this.description = description;
}
public String getKey() {
return key;
}
public String getDescription() {
return description;
}
}
- 測(cè)試代碼
@DisplayName("multipart get config")
@SpringBootTest
public class MultipartGetConfigTest {
private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class);
@Autowired
private ConfigByEnvironment configByEnvironment;
@Test
public void getByEnvironment(){
configByEnvironment.get(ConfigByEnvironmentKey.SERVER_PORT.getKey()).ifPresent(value -> log.info("get by environment, value: {}", value));
}
}
- 測(cè)試結(jié)果
org.spring.demo.MultipartGetConfigTest : get by environment, value: 7100
總結(jié)
| 獲取配置方式 | 優(yōu)點(diǎn) | 缺點(diǎn) | 使用場(chǎng)景 |
|---|---|---|---|
| 通過 @Value 動(dòng)態(tài)獲取單個(gè)配置 | 使用簡(jiǎn)單,且使用關(guān)聯(lián)的鏈路較短 | 1. 配置名不能被有效枚舉到<br />2. 每一個(gè)配置的使用都需重新定義,使用較為麻煩<br />3. 項(xiàng)目強(qiáng)依賴配置的定義,配置不存在則會(huì)導(dǎo)致項(xiàng)目無法啟動(dòng) | 1. 項(xiàng)目強(qiáng)依賴該配置的加載,想要從源頭避免因配置缺失導(dǎo)致的未知問題<br />2. 只想使用少數(shù)幾個(gè)配置 |
| 通過 @ConfigurationProperties + 前綴方式批量獲取 | 1. 使用配置只需確定 key 的前綴即能使用,有利于批量獲取場(chǎng)景的使用 <br />2. 因采用前綴匹配,所以在使用新的相同前綴 key 的配置時(shí)無需改動(dòng)代碼 | 1. 使用復(fù)雜,需定義配置類或者手動(dòng)創(chuàng)建 bean 后引入使用<br />2. 增加新的前綴相同 key 時(shí)可能會(huì)引入不穩(wěn)定因素 | 1. 需要同時(shí)使用多前綴相同 key 的配置<br />2. 期望增加新配置但不修改代碼的 properties 注入 |
| 通過 Environment 動(dòng)態(tài)獲取單個(gè)配置 | 1. 獲取的配置的 key 可不提前定義,程序靈活性高<br />2. 配置 key 可使用枚舉統(tǒng)一放置與管理 | 1. 使用較復(fù)雜,需繼承 Environment 接口形成工具類進(jìn)行獲取<br />2. 獲取 key 對(duì)應(yīng)的枚舉與 key 定義分離,value 獲取鏈路較長(zhǎng) | 1. 只需使用少量的配置<br />2. 獲取配置的 key 無提前定義,需要根據(jù)對(duì)配置的有無進(jìn)行靈活使用 |
本代碼示例已放置于 github,測(cè)試代碼位置,有需要的可自取。
本文首發(fā)于 cartoon的博客