一、SpringEL-基礎介紹
什么是SpringEL(SpEL)?
- Spring3中引入了Spring表達式語言—SpringEL,SpEL是一種強大,簡潔的裝配Bean的方式
- SpringEL可以通過運行期間執行的表達式將值裝配到我們的屬性或構造函數當中
- SpringEL可以調用JDK中提供的靜態常量,獲取外部Properties文件中的的配置
為什么要使用SpringEL?
- 平常通過配置文件或Annotaton注入的Bean,其實都可以稱為靜態性注入
- 如Bean A中有變量A,它的值需要根據Bean B的B變量為參考,在這場景下靜態注入就對這樣的處理顯得非常無力
- 而Spring3增加的SpringEL就可以完全滿足這種需求,而且還可以對不同Bean的字段進行計算再進行賦值,功能非常強大
如何使用SpringEL?
- SpringEL從名字來看就能看出和EL是有點關系的,SpringEL的使用和EL表達式的使用非常相似
- EL表達式在JSP頁面更方便的獲取后臺中的值,而SpringEL就是為了更方便獲取Spring容器中的Bean的值
- EL使用${},而SpringEL使用#{}進行表達式的聲明
兩者主要區別
- $是去找外部配置的參數,將值賦過來
- #是SpEL表達式,去尋找對應變量的內容
- 也可以直接使用@value("常量")注入不使用EL,這樣寫法與直接賦值等價
如果是在Spring中使用可以使用**@PropertySource("classpath:my.properties")**加載對應配置文件
二、EL表達式-基礎使用
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 配置文件 com: codecoord: el: num: 1001 name: el language: - java - spring - mysql - linux # 逗號分隔可以注入列表 language02: java,spring,mysql,linux |
使用EL注入簡單值
1
2
3
4
5
6
|
/** * 注入簡單值,直接注入不使用EL,EL不支持直接指定常量 * 直接在EL中指定的常量會當做配置處理,和直接賦值等價 */ @Value ( "1432516744" ) private Integer no; |
注入配置文件屬性值
1
2
3
4
5
6
7
8
9
10
|
/** * 注入整型屬性值 */ @Value ( "${com.codecoord.el.num}" ) private Integer num; /** * 注入字符屬性值 */ @Value ( "${com.codecoord.el.name}" ) private String name; |
注入默認值
1
2
3
4
5
6
|
/** * 注入字符不存在屬性值并指定默認值,默認值使用過冒號分隔 : * 注入常量其實就可以指定一個不存在的配置然后使用默認值,此處skill的值為java */ @Value ( "${com.codecoord.el.skill:java}" ) private String skill; |
注入列表
- 不支持直接配置文件中數組語法格式注入列表
- 可以識別使用逗號,分隔的配置,spring默認以,分隔
1
2
3
4
5
|
// 錯誤寫法:不支持直接注入yml列表格式語法列表 @Value ( "${com.codecoord.el.language}" ) private List<String> listLanguage; @Value ( "${com.codecoord.el.language}" ) private String[] strLanguage; |
1
2
3
4
5
6
7
|
/** * 支持,分隔的注入列表 */ @Value ( "${com.codecoord.el.language02}" ) private List<String> listLanguage02; @Value ( "${com.codecoord.el.language02}" ) private String[] strLanguage02; |
完整參考如下
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
server: port: 8888 com: codecoord: el: num: 1001 name: el language: - java - spring - mysql - linux # 逗號分隔可以注入列表 language02: java,spring,mysql,linux |
屬性配置類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.List; @Data @Component public class ElConfig { /** * 注入簡單值,直接注入不使用EL,EL不支持直接指定常量 * 直接在EL中指定的常量會當做配置處理,和直接賦值等價 */ @Value ( "1432516744" ) private Integer no; /** * 注入整型屬性值 */ @Value ( "${com.codecoord.el.num}" ) private Integer num; /** * 注入字符屬性值 */ @Value ( "${com.codecoord.el.name}" ) private String name; /** * 注入字符不存在屬性值并指定默認值,默認值使用過冒號分隔 : * 注入常量其實就可以指定一個不存在的配置然后使用默認值,此處skill的值為java */ @Value ( "${com.codecoord.el.skill:java}" ) private String skill; /// 不支持直接注入列表 /*@Value("${com.codecoord.el.language}") private List<String> listLanguage; @Value("${com.codecoord.el.language}") private String[] strLanguage;*/ /** * 支持,分隔的注入列表 */ @Value ( "${com.codecoord.el.language02}" ) private List<String> listLanguage02; @Value ( "${com.codecoord.el.language02}" ) private String[] strLanguage02; } |
向controller中注入配置類,然后訪問接口測試結果如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
{ "no" : 1432516744, "num" : 1001, "name" : "el" , "skill" : "java" , "listLanguage02" : [ "java" , "spring" , "mysql" , "linux" ], "strLanguage02" : [ "java" , "spring" , "mysql" , "linux" ] } |
三、SpringEL-基礎使用
1、使用SpEL注入簡單值和普通EL注入使用基本一致
2、SpEl注入map
- 配置文件中需要使用雙引號括起來,否則將會注入失敗,key為單引號
1
2
3
|
# SpEl spEl: mapInject: "{'name': 'SpEl', 'website': 'http://www.codeocord.com'}" |
java類中先使用${spEl.mapInject}注入字符串值,#{}會解析字符串的值轉為map
1
2
|
@Value ( "#{${spEl.mapInject}}" ) private Map<String, String> mapInject; |
3、SpEl注入list
- 除了可以通過EL注入listI外,也可以使用#{${}.split('分隔符')}的方式注入List
- 配置文件中例如使用#分隔
1
2
|
spEl: listInject: "44#11#99#100" |
java類中先使用${spEl.listInject}注入字符串值,內容使用單引號括起來,然后對字符串使用split方法分隔
提示:避免為空情況,可以給一個默認值空串
1
2
|
@Value ( "#{'${spEl.listInject:}'.split('#')}" ) private List<String> listInject; |
4、動態注入
上述注入都是靜態注入,SpEl支持從Spring容器中注入信息,稱為動態注入。動態注入類如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
import lombok.AllArgsConstructor; import lombok.Data; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Component @Data public class SpElConstant { private String name = "SpElConstant-name" ; private String nickname = "tianxin" ; private int num = 100 ; private List<String> product = new ArrayList<String>() {{ add( "huaweiMate30Pro" ); add( "xiaomi10x5g" ); }}; private Map<String, String> productMap = new HashMap<String, String>() {{ put( "huaweiMate30Pro" , "5999" ); put( "xiaomi10x5g" , "4999" ); }}; private List<City> cityList = new ArrayList<City>() {{ add( new City( "深圳" , 1000L)); add( new City( "杭州" , 2000L)); add( new City( "貴陽" , 900L)); }}; public String showProperty() { return "showProperty-無參數" ; } public String showProperty(String name) { return "showProperty-" + name; } @Data @AllArgsConstructor static class City { private String name; private long population; } } |
SpEl支持和不支持操作
- 支持動態注入實例,類似于對象自動注入
- SPL不支持直接注入配置文件中的配置
-
支持調用靜態和實例方法
- 靜態方法:@Value("#{T(package.ClassName).ConstFieldName")
- 支持調用靜態類或常量
- 支持運算符運算
- 支持操作集合
- 支持查詢篩選集合和投影
注入完整操作如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; @Data @Component public class SpElConfig { /// 不支持直接注入配置文件值 /*@Value("#{com.codecoord.el.num}") private Integer num;*/ /** * 對象注入 */ @Value("#{spElConstant}") private SpElConstant spElConstant; /** * 注入ID為spElConstant Bean中的STR常量/變量 */ @Value("#{spElConstant.name}") private String name; /** * 調用無參方法 */ @Value("#{spElConstant.showProperty()}") private String method1; /** * 有參接收字符串的方法 */ @Value("#{spElConstant.showProperty('Hell SpringEL')}") private String method2; /** * 方法返回的String為大寫 */ @Value("#{spElConstant.showProperty().toUpperCase()}") private String method3; /** * 若使用method3這種方式,若果showProperty返回為null * 將會拋出NullPointerException,可以使用以下方式避免 * 使用?.符號代表若然左邊的值為null,將不執行右邊方法 */ @Value("#{spElConstant.showProperty()?.toUpperCase()}") private String method4; /** * 注入math常量 */ @Value("#{T(java.lang.Math).PI}") private double pi; /** * 用random方法獲取返回值 */ @Value("#{T(java.lang.Math).random()}") private double random; /** * 獲取文件路徑符號 */ @Value("#{T(java.io.File).separator}") private String separator; /** * 拼接字符串 */ @Value("#{spElConstant.nickname + ' ' + spElConstant.name}") private String concatString; /** * 對數字類型進行運算,spElConstant擁有num屬性 */ @Value("#{3 * T(java.lang.Math).PI + spElConstant.num}") private double operation; /** * 進行邏輯運算 */ @Value("#{spElConstant.num > 100 and spElConstant.num <= 200}") private boolean logicOperation; /** * 進行或非邏輯操作 */ @Value("#{not (spElConstant.num == 100) or spElConstant.num <= 200}") private boolean logicOperation2; /** * 使用三元運算符 */ @Value("#{spElConstant.num > 100 ? spElConstant.num : spElConstant.num + 100}") private Integer logicOperation3; /** * 獲取下標為0的元素 */ @Value("#{spElConstant.product[0]}") private String str; /** * 獲取下標為0元素的大寫形式 */ @Value("#{spElConstant.product[0]?.toUpperCase()}") private String upperStr; /** * 獲取map中key為hello的value */ @Value("#{spElConstant.productMap['hello']}") private String mapValue; /** * 根據product下標為0元素作為key獲取testMap的value */ @Value("#{spElConstant.productMap[spElConstant.product[0]]}") private String mapStrByproduct; /** * 注入人口大于等于1000人口的城市 */ @Value("#{spElConstant.cityList.?[population >= 1000]}") private List<SpElConstant.City> cityList; /** * 注入人口等于900人口的城市 */ @Value("#{spElConstant.cityList.?[population == 900]}") private SpElConstant.City city; /** * 注入人口大于等于1000人口的城市,且只保留城市名稱 */ @Value ( "#{spElConstant.cityList.?[population >= 1000].![name]}" ) private List<String> cityName; } |
注入結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
{ "spElConstant" : { "name" : "SpElConstant-name" , "nickname" : "tianxin" , "num" : 100, "product" : [ "huaweiMate30Pro" , "xiaomi10x5g" ], "productMap" : { "xiaomi10x5g" : "4999" , "huaweiMate30Pro" : "5999" }, "cityList" : [ { "name" : "深圳" , "population" : 1000 }, { "name" : "杭州" , "population" : 2000 }, { "name" : "貴陽" , "population" : 900 } ] }, "name" : "SpElConstant-name" , "method1" : "showProperty-無參數" , "method2" : "showProperty-Hell SpringEL" , "method3" : "SHOWPROPERTY-無參數" , "method4" : "SHOWPROPERTY-無參數" , "pi" : 3.141592653589793, "random" : 0.19997238292235787, "separator" : "\\" , "concatString" : "tianxin SpElConstant-name" , "operation" : 109.42477796076938, "logicOperation" : false , "logicOperation2" : true , "logicOperation3" : 200, "str" : "huaweiMate30Pro" , "upperStr" : "HUAWEIMATE30PRO" , "mapValue" : null , "mapStrByproduct" : "5999" , "cityList" : [ { "name" : "深圳" , "population" : 1000 }, { "name" : "杭州" , "population" : 2000 } ], "city" : { "name" : "貴陽" , "population" : 900 }, "cityName" : [ "深圳" , "杭州" ] } |
Spring操作外部Properties文件
1
2
3
|
<!-- 首先通過applicaContext.xml中<util:properties>增加properties文件 --> <!-- 注意需要引入Spring的util schemea命名空間和注意id屬性,id屬性將在SpringEL中使用 --> < util:properties id = "db" location = "classpath:application.properties" /> |
1
2
3
4
5
|
public class TestSpringEL { // 注意db為xml文件中聲明的id @Value ( "#{db['jdbc.url']}" ) private String propertiesValue; } |
SpringEL在使用時僅僅是一個字符串,不易于排錯與測試,也沒有IDE檢查我們的語法,當出現錯誤時較難檢測,復雜的表達式不建議通過SpringEL方式注入。在非必要情況下,不推薦使用SpEl的復雜注入,清晰可讀的代碼更為重要且有利于排查問題
四、屬性自動注入
上述都是通過指定字段進行注入,可以通過@ConfigurationProperties指定前綴進行自動注入
1
|
org.springframework.boot.context.properties.ConfigurationProperties |
配置類
1
2
3
4
5
6
|
user: id: ${random.uuid} name: autowire address: unknown website: www.codecoord.com age: ${random.int} |
自動屬性注入類
- 通過prefix指定前端為user,然后將會把user.后的類型按照名稱進行注入
- 注意必須要提供setter方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties (prefix = "user" ) @Data public class UserConfig { private String id; private String name; private String address; private String website; private Integer age; } |
可以通過@EnableConfigurationProperties(value = UserConfig.class)將UserConfig再次強制注入,問題出現在如果UserConfig為第三方jar包內的配置類,則可能出現屬性沒有注入情況,所以可以指定注入
到此這篇關于SpringBoot SpringEL表達式的使用的文章就介紹到這了,更多相關SpringEL表達式內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://juejin.cn/post/6987993458807930893