Spring 中的 @Value 注解
1、概览
本文将带你了解 Spring 中的 @Value
注解。它用于向 Spring 管理的 Bean 中的字段注入值,可在字段或构造函数/方法上注解。
2、应用设置
创建一个简单的 Spring 应用来介绍该注解的不同用法。
首先,在 Properties 文件中定义想用 @Value
注解注入的值。
Properties 文件如下:
value.from.file=Value got from the file
priority=high
listOfValues=A,B,C
然后,在配置类中定义一个 @PropertySource
,指定 Properties 文件的名称以导入该 Properties 文件。
3、示例
通过注解注入 String
(这种行为完全是多此一举,这里只是为了演示):
@Value("string value")
private String stringValue;
使用 @PropertySource
注解导入了 Properties 文件后,就可以使用 @Value
注解处理 Properties 文件中的值。
如下,注入 value.from.file
属性值:
@Value("${value.from.file}")
private String valueFromFile;
也可以使用相同的语法从系统属性中设置值。
假设定义了一个名为 systemValue
的系统属性,如下:
@Value("${systemValue}")
private String systemValue;
可以给未定义的属性提供默认值,如下:
@Value("${unknown.param:some default}")
private String someDefault;
如果同一个属性在系统属性和 Properties 文件中都被定义了,那么系统属性将会生效。
假设将一个属性 priority
定义为系统属性,其值为 System property,而在 Properties 文件中定义为其他属性。其最终注入值是 System property:
@Value("${priority}")
private String prioritySystemProperty;
有时,我们需要注入一堆值。在 Properties 文件中将它们定义为以逗号分隔的单个属性值,或者定义为系统属性并注入到数组中会比较方便。
Properties 文件的 listOfValues
中定义了以逗号分隔的值,因此如下数组值为 ["A", "B", "C"]
:
@Value("${listOfValues}")
private String[] valuesArray;
4、使用 SpEL 的高级示例
还可以使用 SpEL 表达式来获取值。
如下,如果有一个名为 priority
的系统属性,那么它的值就会应用到字段中:
@Value("#{systemProperties['priority']}")
private String spelValue;
如果没有定义系统属性,则会分配 null
值。
为了避免这种情况,可以在 SpEL 表达式中提供一个默认值。
如下,如果系统属性未定义,就会把字段值设置为 some default:
@Value("#{systemProperties['unknown'] ?: 'some default'}")
private String spelSomeDefault;
此外,还可以使用其他 Bean 的字段值。假设有一个名为 someBean
的 Bean,它的字段 someValue
等于 10。
如下,10 将被分配给该字段:
@Value("#{someBean.someValue}")
private Integer someBeanValue;
可以操作属性来获取一个 List
。如下,结果是字符串值 A、B 和 C 的 List:
@Value("#{'${listOfValues}'.split(',')}")
private List<String> valuesList;
5、Map 注入
可以使用 @Value
注解来注入 Map
属性。
需要在 Properties 文件中以 {key: 'value'}
的形式定义属性:
valuesMap={key1: '1', key2: '2', key3: '3'}
注意,Map
中的 value 必须使用单引号。
现在,可以将 Properties 文件中的值作为 Map
注入:
@Value("#{${valuesMap}}")
private Map<String, Integer> valuesMap;
如果需要获取 Map 中特定 Key 的 Value,只需在表达式中添加 Key 的名称即可:
@Value("#{${valuesMap}.key1}")
private Integer valuesMapKey1;
如果不确定 Map 是否包含某个 Key,应该选择一个更安全的表达式,它不会抛出异常,但会在找不到 Key 时将值设置为 null
:
@Value("#{${valuesMap}['unknownKey']}")
private Integer unknownMapKey;
还可以为不存在的属性或 Key 设置默认值:
@Value("#{${unknownMap : {key1: '1', key2: '2'}}}")
private Map<String, Integer> unknownMap;
@Value("#{${valuesMap}['unknownKey'] ?: 5}")
private Integer unknownMapKeyWithDefaultValue;
还可以在注入前过滤 Map Entry。
如下,假设只需要获取 value 大于 1 的条目:
@Value("#{${valuesMap}.?[value>'1']}")
private Map<String, Integer> valuesMapFiltered;
还可以使用 @Value
注解注入所有当前系统属性:
@Value("#{systemProperties}")
private Map<String, String> systemPropertiesMap;
6、构造函数注入
使用 @Value
注解时,并不局限于字段注入。还可以将其与构造函数注入一起使用。
如下:
@Component
@PropertySource("classpath:values.properties")
public class PriorityProvider {
private String priority;
@Autowired
public PriorityProvider(@Value("${priority:normal}") String priority) {
this.priority = priority;
}
// Getter 方法
}
在上例中,直接将 priority
注入 PriorityProvider
的构造函数。
注意,还提供了一个默认值,以防找不到该属性。
7、Setter 方法注入
与构造函数注入类似,也可以在 Setter 函数注入中使用 @Value
。
@Component
@PropertySource("classpath:values.properties")
public class CollectionProvider {
private List<String> values = new ArrayList<>();
@Autowired
public void setValues(@Value("#{'${listOfValues}'.split(',')}") List<String> values) {
this.values.addAll(values);
}
// Getter 方法
}
使用 SpEL 表达式向 setValues
方法注入一个 Value List
。
8、Record 注入
Java 14 引入了 Record
,以方便创建不可变类。Spring 自 6.0.6 版起支持 @Value
用于 Record
注入:
@Component
@PropertySource("classpath:values.properties")
public record PriorityRecord(@Value("${priority:normal}") String priority) {}
如上,直接将值注入 Record
的构造函数。
9、总结
本文介绍了在 Spring 中应用如何使用 @Value
注解把配置文件、系统属性以及及用 SpEL 表达式计算出属性注入到类的构造函数、字段、Setter 方法甚至是 Record 的构造函数中。
Ref:https://www.baeldung.com/spring-value-annotation