1、引言
從Spring 3開始引入了Spring表達式語言,它能夠以一種強大而簡潔的方式將值裝配到Bean屬性和構造器參數中,在這個過程中所使用的表達式會在運行時計算得到值。使用SpEL你可以實現超乎想象的裝配效果,這是其他裝配技術很難做到的。它包含了很多特性:
使用bean的ID來引用bean;
調用方法和訪問對象的屬性;
對值進行算術、關系和邏輯運算;
正則表達式匹配;
集合操作;
我們時常用到SpEL表達式,例如Spring Security也支持使用SpEL表達式定義安全限制規則。另外在Spring MVC應用中使用Thymeleaf模版作為視圖的話,那么這些模版可以在SpEL表達式引用模型數據。
2、樣例
我們都知道屬性占位符“${...}”,而SpEL表達式則要放到“#{...}”中,下面這個例子可能是最簡單的SpEL表達式了
#{1}
除去“#{...}”之后的內容就是SpEL表達式了,例子是一個數字常量,這個表達式的計算結果就是數字1。
當然在實際的運用中,我們可能會用到更有意思的表達式如
#{T(System).currentTimeMillis()}
它最終的結果的就是計算當前一刻的毫秒數。T()表達式會將java.lang.System視為Java應用中對應的類型,因此可以調用其static修飾的currentTimeMillis()方法。
SpEL表達式也可以引用其他的bean或者其他bean的屬性。例如,如下的表達式會計算得到ID為sgtPeppers的bean對象的airist屬性。以及通過systemProperties對象引用系統屬性;
#{sgtPeppers.airist}
#{systemProperties['disc.title']}
之前我們使用@Value獲取配置到 .properties文件中的值,在之后我們了解到SpEL表達式后,還可以使用這一的表達式去描述。相應的,我們一樣能夠將SpEL表達式運用在XML的配置文件中
public BlankDisc(
@Value("#{systemProperties['disc.title']}" String title,
@Value("#{systemProperties['disc.artist']}" String artist){
this.title = title;
this.artist = artist;
}
XML文件配置
<bean id="blankDisc" class="soundsystem.BlankDisc"
c:_title="#{systemProperties['disc.title']}"
c:_artist="#{systemProperties['disc.artist']}" />
3、用途總結
A) 表示字面量
我們之前使用表達式表示了一個字面量的例子(#{1}),它實際上還可以表示浮點數、String和Boolean的類型,如下所示。當然這很簡單,后面復雜的會用到它們。
#{3.1415926} //浮點數
#{9.87E4} //科學計數法表示98700
#{'Hello'} //String 類型
#{false} //Boolean 類型
B)引用bean、屬性和方法
SpEL可以通過bean的ID去引用這個bean,我們可以使用SpEL將一個bean裝配到另外一個bean中,此時beanID作為SpEL表達式(本例還是用上面的sgtPeppers)
#{sgtPeppers} //使用這個bean
#{sgtPeppers.artist} //引用bean中的屬性
#{sgtPeppers.selectArtist()} //引用bean中的方法
#{sgtPeppers.selectArtist().toUpperCase()} //方法返回值的操作
當然為了防止這個方法的返回值為空,上面的這個方法可以這樣改
#{sgtPeppers.selectArtist()?.toUpperCase()}
發現表達式中間僅多了一個“?”號,它表示的意思是如果selectArtist()方法的返回值不是null的話就執行大寫操作,否則就不執行大寫操作。
C)在表達式中使用類型
如果要在SpEL中訪問類作用域的方法和常量的話,要依賴T()這個關鍵的運算符。例如,為了在SpEL中表達Java的Math類,需要按照如下的方式使用T()運算符。其中T()運算符的結果將會是一個Class類。如果需要的話,我們甚至可以將其裝配到一個Class類型的bean屬性中。但是T()運算符的真正價值在于它能夠訪問靜態方法和常量
T(java.lang.Math)
T(java.lang.Math).PI //引用PI的值
T(java.lang.Math).random() //獲取0-1的隨機數
D)SpEL運算符
運算符類型 運算符
算術比較 +、-、*、/、%、^
比較運算 <、>、==、<=、>=、lt、gt、rq、le、ge
邏輯運算 and、or、not、|
條件運算 ?: (ternary)、?:(Elvis)
正則表達式 matches
話不多說直接舉例:
#{2*T(java.lang.Math).PI * circle.radius} //圓周長計算
#{T(java.lang.Math).PI * circle.radius^2} //圓面積計算
#{disc.title + 'by' + disc.artist} // + 是連接符
#{counter.total == 100} #{counter.total eq 100} //判斷是否一致,返回true和false
#{counter.total > 100 ? "Winner" : "Loser"} //三元表達式
#{disc.title ?: 'Rattle'} //Elvis,如果是null的話結果則為Rattle
#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9._-]+\\.com'} //正則表達式
E)計算集合
除了上面的內容之類,SpEL還加入了一些關于集合的技巧。最簡單的可能就是引用一個簡單列表中的元素了
#{jukebox.songs[4].title}
這句話表示計算songs集合中的第五個元素(從0開始)的title屬性,這個屬性來源于beanID為jukebox的bean。當然我們還可以豐富一下它的內容,比如說隨機選一首歌:
#{jukebox.songs[T(java.lang.Math).random*jukebox.songs.size()].title}
"[]"運算符用來從集合或者數組中按照索引獲取元素,實際上,它還可以從String中獲取一個字符,例如
#{'This is a test'[3]} //獲取的字符就是 's'
SpEL還提供了查詢運算符(.?[]),它會用來對集合進行過濾,得到集合的一個子集。比如我們現在想要從jukebox中artist屬性為Aerosmith的所有歌曲
可以看到選擇運算符在它的 ’ [] ‘ 中接受了另外一個表達式。當SpEL表達式迭代歌曲列表的時候,會對歌曲列表中的每一個條目計算這個表達式。如果表達式的計算結果為true的時候,那么條目會放到新的集合中去。否則的話,它就不會放到新的集合中去。
除了“.?[]”之外,還有兩種查詢運算符“.^[]” 、“.$[]”,他們分別用來在集合中查詢第一個匹配項和最后一個匹配項。
#{jukebox.songs.?[artist eq 'Aerosmith']} //匹配全部
#{jukebox.songs.^[artist eq 'Aerosmith']} //匹配第一個
#{jukebox.songs.$[artist eq 'Aerosmith']} //匹配最后一個
最后,SpEL還提供了投影運算符(.![]),它會從集合的每個成員中選擇特定的屬性放到另外一個集合中。作為樣例,假設我們不想要歌曲對象的集合,而是要獲取所有歌曲的名稱的集合。如下的表達式會替我們完成將title屬性投影到一個新的String類型的集合中:
#{jukebox.songs.![title]}
這個運算符一樣可以和其他的運算符一起使用。比如我們可以使用如下的表達式獲取Aerosmith所有歌曲的名稱列表:
#{jukebox.songs.?[aitist eq 'Aerosmith'].![title]}
到目前只是一些皮毛,僅供參考。