1. 程式人生 > 其它 >SpringBoot 的@Value註解真是太強了,誰用誰說爽!

SpringBoot 的@Value註解真是太強了,誰用誰說爽!

SpringBoot 的@Value註解真是太強了,誰用誰說爽!

一、前言

在日常開發中,經常會遇到需要在配置檔案中,儲存List或是Map這種型別的資料。Spring 原生是支援這種資料型別的,以配置List型別為例,對於.yml檔案配置如下:

test:
list:
-aaa
-bbb
-ccc

對於.properties檔案配置如下所示:

test.list[0]=aaa
test.list[1]=bbb
test.list[2]=ccc

當我們想要在程式中使用時候,想當然的使用@Value註解去讀取這個值,就像下面這種寫法一樣:

@Value("${test.list}")
privateList<String>testList;

你會發現程式直接報錯了,報錯資訊如下:

java.lang.IllegalArgumentException:Couldnotresolveplaceholder'test.list'invalue"${test.list}"

這個問題也是可以解決的,以我們要配置的 key 為test.list為例,新建一個test的配置類,將list作為該配置類的一個屬性:

@Configuration
@ConfigurationProperties("test")
publicclassTestListConfig{
privateList<String>list;

publicList<String>getList(){
returnlist;
}

publicvoidsetList(List<String>list){
this.list=list;
}
}

在程式其他地方使用時候。採用自動注入的方式,去獲取值:

@Autowired
privateTestListConfigtestListConfig;

//testListConfig.getList();

可以看見,這種方式十分的不方便,最大的問題是配置和程式碼高耦合了,增加一個配置,還需要對配置類做增減改動。

整理的spring學習筆記分享給大家:176頁Spring學習筆記,完整版

二、陣列怎麼樣

陣列?說實話,業務程式碼寫多了,這個“古老”的資料結構遠遠沒有 list 用的多,但是它在解決上面這個問題上,出乎異常的好用。

test:
array1:aaa,bbb,ccc
array2:111,222,333
array3:11.1,22.2,33.3
@Value("${test.array1}")
privateString[]testArray1;

@Value("${test.array2}")
privateint[]testArray2;

@Value("${test.array3}")
privatedouble[]testArray3;

這樣就能夠直接使用了,就是這麼的簡單方便,如果你想要支援不配置 key 程式也能正常執行的話,給它們加上預設值即可:

@Value("${test.array1:}")
privateString[]testArray1;

@Value("${test.array2:}")
privateint[]testArray2;

@Value("${test.array3:}")
privatedouble[]testArray3;

僅僅多了一個:號,冒號後的值表示當 key 不存在時候使用的預設值,使用預設值時陣列的 length = 0。總結下使用陣列實現的優缺點:

優點:

  • 不需要寫配置類
  • 使用逗號分割,一行配置,即可完成多個數值的注入,配置檔案更加精簡

缺點*:

  • 業務程式碼中陣列使用很少,基本需要將其轉換為 List,去做 contains、foreach 等操作。

三、替代方法

那麼我們有沒有辦法,在解析 list、map 這些型別時,像陣列一樣方便呢?答案是可以的,這就依賴於EL表示式。

3.1 解析 List

以使用.yml檔案為例,我們只需要在配置檔案中,跟配置陣列一樣去配置:

test:
list:aaa,bbb,ccc

在呼叫時,藉助EL表示式的split()函式進行切分即可。

@Value("#{'${test.list}'.split(',')}")
privateList<String>testList;

同樣,為它加上預設值,避免不配置這個 key 時候程式報錯:

@Value("#{'${test.list:}'.split(',')}")
privateList<String>testList;

但是這樣有個問題,當不配置該 key 值,預設值會為空串,它的 length = 1(不同於陣列,length = 0),這樣解析出來 list 的元素個數就不是空了。!

這個問題比較嚴重,因為它會導致程式碼中的判空邏輯執行錯誤。這個問題也是可以解決的,在split()之前判斷下是否為空即可。

@Value("#{'${test.list:}'.empty?null:'${test.list:}'.split(',')}")
privateList<String>testList;

如上所示,即為最終的版本,它具有陣列方式的全部優點,且更容易在業務程式碼中去應用。

3.2 解析 Set

解析 Set 和解析 List 本質上是相同的,唯一的區別是 Set 會做去重操作。

test:
set:111,222,333,111
`@Value("#{'${test.set:}'.empty?null:'${test.set:}'.split(',')}")
privateSet<Integer>testSet;

//output:[111,222,333]

3.3 解析Map

解析 Map 的寫法如下所示,value 為該 map 的 JSON 格式,注意這裡使用的引號:整個 JSON 串使用引號包裹,value 值使用引號包裹。

test:
map1:'{"name":"zhangsan","sex":"male"}'
map2:'{"math":"90","english":"85"}'

在程式中,利用 EL 表示式注入:

@Value("#{${test.map1}}")
privateMap<String,String>map1;

@Value("#{${test.map2}}")
privateMap<String,Integer>map2;

注意,使用這種方式,必須得在配置檔案中配置該 key 及其 value。我在網上找了許多資料,都沒找到利用 EL 表示式支援不配置 key/value 的寫法。如果你真的很需要這個功能,就得自己寫解析方法了,這裡以使用 fastjson 進行解析為例:
(1) 自定義解析方法

publicclassMapDecoder{
publicstaticMap<String,String>decodeMap(Stringvalue){
try{
returnJSONObject.parseObject(value,newTypeReference<Map<String,String>>(){});
}catch(Exceptione){
returnnull;
}
}
}

(2) 在程式中指定解析方法

@Value("#{T(com.github.jitwxs.demo.MapDecoder).decodeMap('${test.map1:}')}")
privateMap<String,String>map1;

@Value("#{T(com.github.jitwxs.demo.MapDecoder).decodeMap('${test.map2:}')}")
privateMap<String,String>map2;

四、後續

以上就是本文的全部內容,利用 EL 表示式、甚至是自己的解析方法,可以讓我們更加方便的配置和使用 Collection 型別的配置檔案。特別注意的是@Value註解不能和@AllArgsConstructor註解同時使用,否則會報錯

Considerdefiningabeanoftype'java.lang.String'inyourconfiguration

這種做法唯一不優雅的地方就是,這樣寫出來的@Value的內容都很長,既不美觀,也不容易閱讀。