知识点
Spring提供了@Value
注解,可用于将配置文件或注册中心的属性值动态注入到Bean
中。
注:
@Value
注解在spring-beans
包里。
@Value("${...}")
:注入获取对应属性文件中定义的属性值;@Value("#{...}")
:表示SpEl表达式通常用来获取Bean
的属性;
实例
/**
* 服务内动态配置
*
* @author cdfive
*/
@Slf4j
@Getter
@Setter
@RefreshScope
@Configuration
public class DynamicConfig implements Serializable {
// 服务内部错误代码
@Value("${xxx.dynamic.serviceError.code:500}")
private Integer serviceErrorCode;
// 服务内部错误消息
@Value("${xxx.dynamic.serviceError.msg:服务繁忙,请稍候再试}")
private String serviceErrorMsg;
// RPC日志是否启用
@Value("${xxx.dynamic.rpcLogEnable:true}")
private boolean rpcLogEnable;
// 业务内部线程池核心线程数
@Value("${xxx.dynamic.bizInteralThreadPool.coreSize:4}")
private int bizInteralThreadPoolCoreSize;
...
// 跟踪的商品编码集合
@Value("${xxx.dynamic.trace.productCode:#{null}}")
private String traceProductCode;
通过@Value("${xxx}")
为Bean
里的字段映射配置文件的值,如application.yml
:
xxx:
dynamic:
serviceError:
code: 500
msg: 系统维护中,请稍候再试
trace:
productCode: 100000001
注意点1---默认值
如果配置文件里没有xxx
的配置,则@Value("${xxx}")
需要指定默认值,否则启动应用报错。
例如:
@Value("${error.code.msg}")
private Integer errorCodeMsg;
应用启动报错:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'error.code.msg' in value "${error.code.msg}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
通过:
分隔后面加上默认值。
例如:
@Value("${error.code.success:200}")
private Integer errorCodeSuccess;
如果外部没有配置codeSuccess
,则它的默认值为200。
当属性为Integer
、String
、Boolean
对象类型,如果希望外部没有配置时,默认为null
,则在:
后加上#{null}
。
例如:
@Value("${error.code.msg:#{null}}")
private String errorCodeMsg;
如果只写:
,String
类型默认值为空字符串""
;
例如:
@Value("${error.code.msg:}")
private String errorCodeMsg;
注:对于Integer
、Long
类型,xxx:#{null}
和xxx:
效果相同,默认值为null
。
注意点2---集合类型
List
,Set
类型,集合每一项值通过英文逗号,
分隔
@Value("${global.config.nums}")
private List<Integer> nums;
@Value("${global.config.items}")
private Set<String> items;
global:
config:
nums: 1,2,3
items: aa,bb,cc
如果不想在yml
配置,可在Bean
里指定默认值:
@Value("${global.config.nums:1,2,3}")
private List<Integer> nums;
@Value("${global.config.items:aa,bb,cc}")
private Set<String> items;
如果想Bean
的集合字段默认值为null
,等有需要时再去yml里配置,则加上:#{null}
即可。
@Value("${global.config.nums:#{null}}")
private List<Integer> nums;
@Value("${global.config.items:#{null}}")
private Set<String> items;
如果想Bean
的集合字段默认值为空集合,等有需要时再去yml里配置,则加上:
即可。
@Value("${global.config.nums:}")
private List<Integer> nums;
@Value("${global.config.items:}")
private Set<String> items;
默认空集合可不用null
判断,避免NPE
空指针异常。
注意点3---SpEL表达式
使用split
方法,英文逗号分隔
@Value("#{'${xxx.dynamic.codes:111,222}'.split(',')}")
private List<String> codes;
注意这种方式:
1.如果不填默认值,且外部配置没有:
@Value("#{'${xxx.dynamic.codes}'.split(',')}")
private List<String> codes;
则启动报错Could not resolve placeholder
2.默认值里不能用#{null}
@Value("#{'${xxx.dynamic.codes:#{null}}'.split(',')}")
private List<String> codes;
整个会被当作字符串解析,解析结果为列表里只有1个元素,值为#{null}
3.写:
也不行
@Value("#{'${xxx.dynamic.codes:#{null}}'.split(',')}")
private List<String> codes;
解析结果为列表里只有1个元素,值为空字符串""
如果想代码里不填默认值,且允许外部不配置,此时列表为空列表:
可考虑自定义方法decodeList
:
@SuppressWarnings("rawtypes")
public static List<String> decodeList(String value) {
if (StringUtils.isBlank(value)) {
return Collections.emptyList();
}
return Splitter.on(",").omitEmptyStrings().splitToList(value).stream().collect(Collectors.toList());
}
通过#{T(包名.类名).方法名('${...}')}
使用自定义方法:
@Value("#{T(xxx.DynamicConfig).decodeList('${xxx.dynamic.codes}')}")
private List<String> codes;
Map类型,自定义方法decodeMap
@SuppressWarnings("rawtypes")
public static Map decodeMap(String value) {
if (StringUtils.isBlank(value)) {
return Collections.emptyMap();
}
return JsonUtils.json2Obj(value, Map.class);
}
属性设置:
@Value("#{T(xxx.DynamicConfig).decodeMap('${xxx.dynamic.channelTypes}')}")
private Map<String, List<String>> channelTypes;
外部配置:
xxx:
dynamic:
channelTypes: '{"aa":["xx","yy"],"bb":["zz"]}'
补充1
SpringBoot在Spring的基础上,提供了更多的注解用于配置管理,如:@ConfigurationProperties
,EnableConfigurationProperties
等。
例:
@Data
@ConfigurationProperties(prefix = "xxx.config")
public class XxxProperties {
private Boolean enable;
private String code;
...
}
@Slf4j
@Configuration
@EnableConfigurationProperties(XxxProperties.class)
public class XxxAutoConfig {
...
}
其中:
@ConfigurationProperties(prefix = "xxx.config")
把类的属性与yml配置文件里配置进行绑定;@EnableConfigurationProperties(XxxProperties.class)
,将XxxProperties.class
添加至Spring容器;- 也可不使用
@EnableConfigurationProperties
,那么XxxProperties.class
需要通过其他方式添加至Spring容器,如标记注解@Component
补充2
外部配置,如项目里的application.yml
文件、或者Nacos配置中心里yml配置,如果属性类型为String
,属性值为0开头,则属性值需要加上双引号。
配置类:
@Value("${xxx.config.codes}")
private String code;
yml文件:
xxx:
config:
code: 048
项目启动,发现code
值不是期望的048
,而是48.0
;
修改yml文件:
xxx:
config:
code: "048"
再次重启项目,code
值为预期的048