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

到目前只是一些皮毛,仅供参考。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,302评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,563评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,433评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,628评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,467评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,354评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,777评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,419评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,725评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,768评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,543评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,387评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,794评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,032评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,305评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,741评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,946评论 2 336

推荐阅读更多精彩内容