Spring注解之@Value注解讀取配置文件屬性和設(shè)置默認(rèn)值
概述
??在Spring 組件中,通常使用@Value注解讀取 properties 文件的配置值。但如果在配置文件或啟動參數(shù)中未指定對應(yīng)的參數(shù)值,則項(xiàng)目在啟動的時候會拋出異常,導(dǎo)致服務(wù)啟動失敗,異常信息往往提示缺少必要的屬性配置信息:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'self.user.address' in value "${self.user.address}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-5.3.7.jar:5.3.7]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:936) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.7.jar:5.3.7]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.7.jar:5.3.7]
... 17 common frames omitted
??解決辦法是在Apollo等配置文件中對@Value對應(yīng)的值進(jìn)行配置,或設(shè)置默認(rèn)值。本文介紹@Value注解的語法糖,介紹如何設(shè)置key的默認(rèn)值,介紹如何配置數(shù)組、列表和map的初始值。
@Value注解語法糖
??注解@Value用于讀取配置文件中的屬性,語法糖有以下三種。
(1) 直接賦值
@Value("string value")
??這種方式就是直接把要注入的值字面量寫在注解里,比較少用。如果要寫死在注解里了,那直接定義變量的時候?qū)懰谰涂梢粤恕?/p>
(2) 使用占位符$
@Value(${key : default_value})
??這是最常用的姿勢,通過屬性名的key,將值注入變量。default_value為默認(rèn)值。$注入的是配置文件對應(yīng)的property,使用英文冒號“:”對未配置或值為空的key設(shè)置默認(rèn)值。
(3)SpEL表達(dá)式
@Value(#{self.key?: default_value})
??使用 Spring Expression Language (SpEL) 設(shè)置默認(rèn)值。#注入的是SpEL表達(dá)式對應(yīng)的內(nèi)容,使用“?:”對未配置或值為空的表達(dá)式設(shè)置默認(rèn)值。
??另外,占位符和SpEL表達(dá)式可以雙劍合璧,解鎖方式如下:
@Value("#{'${listOfValues}'.split(',')}")
private List valueList;
??溫馨提示,內(nèi)外順序不能顛倒,必須是#{}外面,${}在里面!
使用場景
??對于注入的場景,主要有三種:
(1)bean聲明的變量,香餑餑級別用法。
(2)setter方法注入,不常用。
(3)構(gòu)造方法或其它方法的入?yún)ⅲ@就是雞肋,不能把關(guān)鍵參數(shù)寫死。
??示例如下:
//bean聲明的變量
public static class MyValues {
@Value("${self.user.name}")
private String userName;
}
//setter 方法中
public static class MyValues {
private String userName;
@Value("${self.user.name}")
public void setUserName(String userName) {
this.userName = userName;
}
}
//方法入?yún)?public class MyValues {
private String userName;
@Autowired
public void configure(@Value("${self.user.name}") String userName) {
this.userName = userName;
}
}
基本類型
??設(shè)置默認(rèn)值時,在key后加上冒號及其默認(rèn)值即可,方法如下:
public class ReadConfig {
// 未指定默認(rèn)值
@Value("${self.user.name}")
private String userName;
// 使用英文冒號指定默認(rèn)值為“defaultValue”
@Value("${self.user.address:defaultValue}")
private String userAddress;
@Value("${self.bool:true}")
private boolean booleanWithDefaultValue;
@Value("${self.user.age:21}")
private Integer userAge;
}
??針對以上第一種@Value注解的使用方式,如果username對應(yīng)的屬性值未在Apollo配置中心、application.properties文件中配置或未在java -jar命令中傳遞參數(shù),那么服務(wù)啟動時將拋出 IllegalArgumentException 異常,導(dǎo)致服務(wù)發(fā)布失敗。而關(guān)于第二種方式,通過“:”指定默認(rèn)值,則可以正常啟動服務(wù)。
數(shù)組和列表
??在配置文件中配置數(shù)組或者列表時,使用的默認(rèn)分隔符是英文逗號,也可以自定義。 demo如下:
self.array = xxx1,xxx2,xxx3
??基于配置文件注入屬性值:
/**
* 注入數(shù)組,默認(rèn)','分隔
*/
@Value("${self.array:one,two,three}")
String[] array;
/**
* 注入列表,分隔符使用英文分號
*/
@Value("#{'${self.empty.array:}'.empty ? null : '${self.empty.array}'.split(';')}")
List<String> list;
設(shè)置map
@Value("#{${self.map1:{name: 'default', age: 18, city: '河南'}}}")
private Map<String, Object> defaultMap;
??map設(shè)置默認(rèn)值也是使用英文冒號。
綜合實(shí)戰(zhàn)
??配置文件配置如下:
self.param.user.name = 樓蘭胡楊
self.array = xxx1,xxx2,xxx3
self.map={name: 'Wiener', age: '18', city: '商丘'}
??測試用例如下:
// 指定默認(rèn)值
@Value("${self.user.name:defaultValue}")
private String userName;
@Value("${self.array}")
private List<String> myList;
@Value("${self.array:one,two,three}")
private String[] myArray;
// 未配置屬性,使用默認(rèn)值空數(shù)組
@Value("${self.empty.array:}")
private String[] myEmptyArray;
// 未配置屬性,使用null
@Value("#{'${self.empty.array:}'.empty ? null : '${self.empty.array}'.split(',')}")
private List<String> myNullList;
@Value("#{${self.map}}")
private Map<String, Object> myMap;
@Value("#{${self.map}.city}")
private String cityValue;
@Value("#{${self.map1:{name: 'default', age: 18, city: '河南'}}}")
private Map<String, Object> defaultMap;
//表達(dá)式結(jié)果
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNumber;
@PostMapping("/readConfig")
public Map<String, Object> readConfig(@Value("${self.param.user.name}") String myUserName) {
System.out.println("雞肋般的傳參 myUserName=" + myUserName);
System.out.println("#````````````````````````# userName=" + userName);
System.out.println("#````````````````````````# myList=" + myList);
System.out.println("#````````````````````````# cityValue=" + cityValue);
System.out.println("#``````使用默認(rèn)值 defaultMap=" + defaultMap);
System.out.println("#````````````````````````# myArray=");
for (String one : myArray) {
System.out.println(one + " 數(shù)組");
}
System.out.println("空數(shù)組myEmptyArray大小:" + myEmptyArray.length);
System.out.println("空列表myNullList是否為null:" + CollectionUtils.isEmpty(myNullList));
System.out.println("random number:" + randomNumber);
return myMap;
}
??執(zhí)行結(jié)果如下:
雞肋般的傳參 myUserName=樓蘭胡楊
#````````````````````````# userName=defaultValue
#````````````````````````# myList=[xxx1, xxx2, xxx3]
#````````````````````````# cityValue=商丘
#``````使用默認(rèn)值 defaultMap={name=default, age=18, city=河南}
#````````````````````````# myArray=
xxx1 數(shù)組
xxx2 數(shù)組
xxx3 數(shù)組
空數(shù)組myEmptyArray大小:0
空列表myNullList是否為null:true
random number:41.28241165389434
小結(jié)
??本文結(jié)合案例講解了@Value注解的使用方法,包括如何設(shè)置數(shù)組、列表和map的默認(rèn)值。
??最后,奉上一個歸納總結(jié),如下圖[1]所示:
??對于Wiener以上的話題,大家又有什么自己的獨(dú)特見解呢?歡迎在下方評論區(qū)留言!
Reference
[1] https://segmentfault.com/a/1190000021415142?utm_source=tag-newest
Buy me a coffee. ?Get red packets.
介紹Spring注解中,@Value注解的常用方法,包括如何為變量設(shè)置數(shù)組、列表和map及其默認(rèn)值。
浙公網(wǎng)安備 33010602011771號