SpEL表達式

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]}

到目前只是一些皮毛,僅供參考。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容