Spring注解之@Value注解讀取配置文件屬性和設置默認值


概述

  在Spring 組件中,通常使用@Value注解讀取 properties 文件的配置值。但如果在配置文件或啟動參數中未指定對應的參數值,則項目在啟動的時候會拋出異常,導致服務啟動失敗,異常信息往往提示缺少必要的屬性配置信息:

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對應的值進行配置,或設置默認值。本文介紹@Value注解的語法糖,介紹如何設置key的默認值,介紹如何配置數組、列表和map的初始值。

@Value注解語法糖

  注解@Value用於讀取配置文件中的屬性,語法糖有以下三種。

(1) 直接賦值

@Value("string value")

  這種方式就是直接把要注入的值字面量寫在注解里,比較少用。如果要寫死在注解里了,那直接定義變量的時候寫死就可以了。

(2) 使用占位符$

@Value(${key : default_value})

  這是最常用的姿勢,通過屬性名的key,將值注入變量。default_value為默認值。$注入的是配置文件對應的property,使用英文冒號“:”對未配置或值為空的key設置默認值。

(3)SpEL表達式

@Value(#{self.key?: default_value})

  使用 Spring Expression Language (SpEL) 設置默認值。#注入的是SpEL表達式對應的內容,使用“?:”對未配置或值為空的表達式設置默認值。

  另外,占位符和SpEL表達式可以雙劍合璧,解鎖方式如下:

@Value("#{'${listOfValues}'.split(',')}")
private List valueList;

  溫馨提示,內外順序不能顛倒,必須是#{}外面,${}在里面!

使用場景

  對於注入的場景,主要有三種:

(1)bean聲明的變量,香餑餑級別用法。

(2)setter方法注入,不常用。

(3)構造方法或其它方法的入參,這就是雞肋,不能把關鍵參數寫死。

  示例如下:

//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;
    }
}

//方法入參
public class MyValues {
    private String userName;
    @Autowired
    public void configure(@Value("${self.user.name}") String userName) {
        this.userName = userName;
    }
}

基本類型

  設置默認值時,在key后加上冒號及其默認值即可,方法如下:

public class ReadConfig {
    // 未指定默認值
    @Value("${self.user.name}")
    private String userName;
    // 使用英文冒號指定默認值為“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對應的屬性值未在Apollo配置中心、application.properties文件中配置或未在java -jar命令中傳遞參數,那么服務啟動時將拋出 IllegalArgumentException 異常,導致服務發布失敗。而關於第二種方式,通過“:”指定默認值,則可以正常啟動服務。

數組和列表

  在配置文件中配置數組或者列表時,使用的默認分隔符是英文逗號,也可以自定義。 demo如下:

self.array = xxx1,xxx2,xxx3

  基於配置文件注入屬性值:

/**
 * 注入數組,默認','分隔
 */
@Value("${self.array:one,two,three}")  
String[] array;

/**
 * 注入列表,分隔符使用英文分號
 */
@Value("#{'${self.empty.array:}'.empty ? null : '${self.empty.array}'.split(';')}")
List<String> list;

設置map

@Value("#{${self.map1:{name: 'default', age: 18, city: '河南'}}}")
private Map<String, Object> defaultMap;

  map設置默認值也是使用英文冒號。

綜合實戰

  配置文件配置如下:

self.param.user.name = 樓蘭胡楊
self.array = xxx1,xxx2,xxx3
self.map={name: 'Wiener', age: '18', city: '商丘'}

  測試用例如下:

// 指定默認值
    @Value("${self.user.name:defaultValue}")
    private String userName;

    @Value("${self.array}")
    private List<String> myList;
    @Value("${self.array:one,two,three}")
    private String[] myArray;
    // 未配置屬性,使用默認值空數組
    @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;
    //表達式結果
    @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("#``````使用默認值 defaultMap=" + defaultMap);
        System.out.println("#````````````````````````# myArray=");
        for (String one : myArray) {
            System.out.println(one + " 數組");
        }
        System.out.println("空數組myEmptyArray大小:" + myEmptyArray.length);
        System.out.println("空列表myNullList是否為null:" + CollectionUtils.isEmpty(myNullList));
        System.out.println("random number:" + randomNumber);
        return myMap;
    }

  執行結果如下:

雞肋般的傳參 myUserName=樓蘭胡楊
#````````````````````````# userName=defaultValue
#````````````````````````# myList=[xxx1, xxx2, xxx3]
#````````````````````````# cityValue=商丘
#``````使用默認值 defaultMap={name=default, age=18, city=河南}
#````````````````````````# myArray=
xxx1 數組
xxx2 數組
xxx3 數組
空數組myEmptyArray大小:0
空列表myNullList是否為null:true
random number:41.28241165389434

小結

  本文結合案例講解了@Value注解的使用方法,包括如何設置數組、列表和map的默認值。

  最后,奉上一個歸納總結,如下圖[1]所示:


Spring @Value 注解

  對於Wiener以上的話題,大家又有什么自己的獨特見解呢?歡迎在下方評論區留言!

Reference

[1] https://segmentfault.com/a/1190000021415142?utm_source=tag-newest


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM