1. 概述
本文我们学习 Spring @Value 注解的用法。该注解用于将常量、配置文件属性、以及其他bean的值注入到Spring bean中。
2. 初始化
方便演示之前,我们先准备一份properties文件,里面是@Value
要注入的值。配置类上需要使用 @PropertySource
指定我们的文件名。
properties文件内容如下:
value.from.file=Value got from the file
priority=high
listOfValues=A,B,C
3. 使用示例
示例一,将字符串(“string value”)直接注入到字段中。这个例子实际没什么用,没人这么干吧 :)。
@Value("string value")
private String stringValue;
使用 @PropertySource
注解可加载配置文件,然后通过@Value
读取配置文件中的值。
@Value("${value.from.file}")
private String valueFromFile;
也可以读取System property。
假设我们定义了一个名为systemValue
的系统属性:
@Value("${systemValue}")
private String systemValue;
对于未定义的属性,我们还可以设置默认值。下面如果没找到名为unknown.param
的属性,将注入默认值some default
:
@Value("${unknown.param:some default}")
private String someDefault;
如果同一属性既在系统属性中定义又在配置文件中定义,则优先取系统属性的值。
例如,如果我们有一个键名为priority
的属性,它在系统属性中值为System property
,而在配置文件中设置为其他值,最后读取是System property
的值:
@Value("${priority}")
private String prioritySystemProperty;
有时,我们需要注入数组类型的值,可用以逗号进行分隔。
例如,前面的配置文件中listOfValues
是以逗号分隔,因此注入的数组值为["A", "B", "C"]
:
@Value("${listOfValues}")
private String[] valuesArray;
4. 高级用法 - SpEL表达式
@Value 注解支持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. 使用@Value
与Map
我们还可以使用@Value
注解注入Map
类型属性。
首先,我们需要在属性文件中以{key: 'value'}
的形式定义属性:
valuesMap={key1: '1', key2: '2', key3: '3'}
请注意,Map
中的值必须用单引号包裹。
现在,我们可以从属性文件中以Map
类型注入这个值:
@Value("#{${valuesMap}}")
private Map<String, Integer> valuesMap;
如果我们需要从Map
中获取特定键的值,只需在表达式中添加键的名字即可:
@Value("#{${valuesMap}.key1}")
private Integer valuesMapKey1;
如果我们不确定Map
是否包含特定键,应该选择一个不会抛出异常但当键不存在时将值设置为null
的安全表达式:
@Value("#{${valuesMap}['unknownKey']}")
private Integer unknownMapKey;
我们还可以为可能不存在的属性或键设置默认值:
@Value("#{${unknownMap : {key1: '1', key2: '2'}}}")
private Map<String, Integer> unknownMap;
@Value("#{${valuesMap}['unknownKey'] ?: 5}")
private Integer unknownMapKeyWithDefaultValue;
在注入前,我们也可以 过滤Map记录。
假设我们需要获取值大于1的记录:
@Value("#{${valuesMap}.?[value>'1']}")
private Map<String, Integer> valuesMapFiltered;
我们还可以使用@Value
注解注入所有当前系统属性:
@Value("#{systemProperties}")
private Map<String, String> systemPropertiesMap;
6. 在构造函数上使用@Value
注解
@Value
注解除了用于字段注入。还可以用于构造函数参数上。
让我们实际看看:
@Component
@PropertySource("classpath:values.properties")
public class PriorityProvider {
private String priority;
@Autowired
public PriorityProvider(@Value("${priority:normal}") String priority) {
this.priority = priority;
}
// standard getter
}
在上述示例中,我们直接将priority
注入到PriorityProvider
构造函数中。
请注意,我们还提供了默认值,以防找不到属性。
7. 使用@Value
与setter注入
类似构造注入,我们也可以使用@Value
与setter注入。
让我们来看一下:
@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);
}
// standard getter
}
我们使用SpEL表达式将一个值列表注入到setValues
方法中。
8. @Value
与Records
Java 14 引入了Records,以方便创建不可变类。自Spring框架6.0.6版本起,它支持@Value
对Record的注入:
@Component
@PropertySource("classpath:values.properties")
public record PriorityRecord(@Value("${priority:normal}") String priority) {}
在这里,我们将值直接注入到与Record的构造函数中。
9. 总结
本文详细介绍了如何在配置文件、系统属性和使用SpEL表达式的属性中使用@Value
注解的各种方法。
惯例,本文中的示例代码可在GitHub中找到。