Spring表达式语言使用
@(Spring)[spEL, Spring, 表达式语言]
基本概述
百度百科。。。。。
spEL的功能概述
The expression language supports the following functionality:
- Literal expressions
- Boolean and relational operators
- Regular expressions
- Class expressions
- Accessing properties, arrays, lists, maps
- Method invocation
- Relational operators
- Assignment
- Calling constructors
- Bean references
- Array construction
- Inline lists
- Inline maps
- Ternary operator
- Variables
- User defined functions
- Collection projection
- Collection selection
- Templated expressions
PS:从官方文档,可以看出spEL是一门非常强大的表达式语言,其不仅支持一般表达式语言常见的显示数据功能,还支持像是正则表达式、对象操作、静态方法、静态字段、集合操作、集合投影、集合筛选等等高级特性。
spEL语法(以XML为例)
spEL使用#{<expression string>}
作为定界符,其中的字符就会被识别为spEL。
字面值表达式
字符串
<property name="str1" value="#{'Hello World'}"/>
<property name='str2' value='#{"Hello World"}'/>
整型
<property name="int" value="#{6}"/>
浮点型
<property name="float" value="#{5.3}"/>
科学计数法
<property name="c" value="#{6.0221415E+23}"/>
boolean类型
<property name="bool" value="#{true}"/>
引用Bean,属性和方法
引用其它对象
<bean id=”saxophone” value=”com.xxx.xxx.Xxx”/>
<bean ..>
<property name="instrument" value="#{saxophone}"/>
<bean/>
通过id:“saxophone”将对象注入到instrument 属性中,这与下面的配置是一样的:
<property name="instrument" ref="saxophone"/>
引用其他对象的属性
<bean id="carl" class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="#{kenny.song}" />
</bean>
kenny 是Bean Id 而 song 是属性的名字,这样配置就如同我们写了如下的代码
Instrumentalist carl = new Instrumentalist();
carl.setSong(kenny.getSong());
调用其他方法
<property name="song" value="songSelector.selectSong()"/>
调用了Bean的id为"songSelector"
的对象的selectSong()
方法,并将返回值注入到song 属性
中。或者还可以链式操作。如下:
<property name="song" value="songSelector.selectSong().toUpperCase()"/>
如果songSelector.selectSong()
返回null
的还会抛出异常,为了避免我们要使用?.表达式。
这样如果songSelector.selectSong()
为null
就不会再调用后面的方法了。如下
<property name="song" value="songSelector.selectSong()?.toUpperCase()"/>
调用静态方法
我们已经知道如何通过一个对象调用它的方法了,但是如何调用一个静态方法呢?用T()
。
它将返回一个 Class object
然后我们再调用相应的方法即可:
<property name="multiplier" value="T(java.lang.Math).PI"/>
SpEL 支持的运算符号
算数运算符:+, -, *, /, %, ^
<property name="adjustedAmount" value="#{counter.total + 42}"/>
<property name="adjustedAmount" value="#{counter.total - 20}"/>
<property name="circumference" value="#{2 * T(java.lang.Math).PI * circle.radius}"/>
<property name="average" value="#{counter.total / counter.count}"/>
<property name="remainder" value="#{counter.total % counter.count}"/>
<property name="area" value="#{T(java.lang.Math).PI * circle.radius ^ 2}"/>
加号还可以用作字符串连接
<property name="fullName" value="#{performer.firstName + ' ' + performer.lastName}"/>
比较运算符: <, >, ==, <=, >=, lt, gt, eq, le, ge
<property name="equal" value="#{counter.total == 100}"/>
不可以使用<和>号,应为在xml 中它有特殊的含义,我们使用lt 和gt 代替
<property name="hasCapacity" value="#{counter.total le 100000}"/>
逻辑运算符号: and, or, not, |
<property name="largeCircle" value="#{shape.kind == 'circle' and shape.perimeter gt 10000}"/>
<property name="outOfStock" value="#{!product.available}"/>
<property name="outOfStock" value="#{not product.available}"/>
If-else 运算符:?: (ternary), ?: (Elvis)
最基本的 ?:(这如同我们在使用EL 表达式语言):
<property name="instrument" value="#{songSelector.selectSong() == 'Jingle Bells' ? piano : ' Jingle Bells '}"/>
变体的 ?:
<property name="song" value="#{kenny.song ?: 'Greensleeves'}"/>
正则表达式:matches
<property name="validEmail" value="#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'}"/>
表达式返回逻辑值,如果匹配返回true,否则返回false
SpEL 对集合的支持
环境
有实体City 定义如下:
package com.habuma.spel.cities;
public class City {
private String name;
private String state;
private int population;
}
Xml 中有如下定义
<util:list id="cities">
<bean class="com.habuma.spel.cities.City" p:name="Chicago" p:state="IL" p:population="2853114"/>
<bean class="com.habuma.spel.cities.City" p:name="Atlanta" p:state="GA" p:population="537958"/>
<bean class="com.habuma.spel.cities.City" p:name="Dallas" p:state="TX" p:population="1279910"/>
<bean class="com.habuma.spel.cities.City" p:name="Houston" p:state="TX" p:population="2242193"/>
<bean class="com.habuma.spel.cities.City" p:name="Odessa" p:state="TX" p:population="90943"/>
<bean class="com.habuma.spel.cities.City" p:name="El Paso" p:state="TX" p:population="613190"/>
<bean class="com.habuma.spel.cities.City" p:name="Jal" p:state="NM" p:population="1996"/>
<bean class="com.habuma.spel.cities.City" p:name=" Cruces" p:state="NM" p:population="91865"/>
</util:list>
获取Collection 中的某个对象
<property name="chosenCity" value="#{cities[2]}"/>
我们就会获得population 为”1279910”的city(记住下标从0 开始)
下标可以通过变量指定,如下:
<property name="chosenCity" value="#{cities[T(java.lang.Math).random() * cities.size()]}"/>
如果是从Map 中获得,可指定key 值,如下
<property name="chosenCity" value="#{cities['Dallas']}"/>
也可以通过key 访问properties 的值,如下
<util:properties id="settings" location="classpath:settings.properties"/>
<property name="accessToken" value="#{settings['twitter.accessToken']}"/>
可以通过下标访问systemEnvironment 和SystemProperties 中的值
<property name="homePath" value="#{systemEnvironment['HOME']}"/>
如果在jre 运行时配置了-Dapplication.home=/etc/myapp
,我们可以通过如下方式访问
<property name="homePath" value="#{systemProperties['application.home']}"/>
通过下标获取String 串中的某个字符
'This is a test'[3]
获取Collection 中的子集-通过条件筛选(注意新对象是一个新的Collection)
筛选子集(.?[])
<property name="bigCities" value="#{cities.?[population gt 100000]}"/>
获取第一个(.^[])
<property name="aBigCity" value="#{cities.^[population gt 100000]}"/>
获取最后一个(.$[])
<property name="aBigCity" value="#{cities.$[population gt 100000]}"/>
集合的投影(.![])
如果想获得所有城市的名称组成的列表,可用如下操作
<property name="cityNames" value="#{cities.![name]}"/>
将返回"Chicago", "Atlanta", "Dallas"
也可以组合两个列,如下:
<property name="cityNames" value="#{cities.![name + ', ' + state]}"/>
将返回"Chicago, IL", "Atlanta, GA", and "Dallas, TX"
将投影和筛选结合
<property name="cityNames" value="#{cities.?[population gt 100000].![name + ', ' + state]}"/>
PS:这部分是从是从某pdf中整理出来的,该pdf是参考别人博客的,没有写明参考,敬请见谅。
基于注解的spEL
使用@Value
注解可以放在域、方法、方法/ 构造方法参数作为默认值。
设置域属性的默认值
public static class FieldValueTestBean {
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
与以下代码等价
public static class PropertyValueTestBean {
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
在方法/构造方法参数中使用
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
// ...
}
public class MovieRecommender {
private String defaultLocale;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
@Value("#{systemProperties['user.country']}") String defaultLocale) {
this.customerPreferenceDao = customerPreferenceDao;
this.defaultLocale = defaultLocale;
}
// ...
}
spEL解析接口及其使用
Spring表达式语言的解析接口为ExpressionParser
,是用来解析spEL表达式的,在org.springframework.expression
包下,其实现类SpelExpressionParser
,在它的子包spel.support
下。
spEL接口使用
通过该接口中的一些方式,我们能在java代码中操纵spEL。其使用方式如下:
String
// 创建对象
ExpressionParser parser = new SpelExpressionParser();
// 解析表达式
Expression exp = parser.parseExpression("'Hello World'");
// 获取结果
String message = (String) exp.getValue();
对象方法调用
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
对象属性使用
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes");
byte[] bytes = (byte[]) exp.getValue();
dot notation链式操作
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length");
int length = (Integer) exp.getValue();
构造方法的使用
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String message = exp.getValue(String.class);
PS:注意,构造方法获取其值时,可以通过public <T> T getValue(Class<T> desiredResultType)
该方法来转换类型。
取对象中的值
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
EvaluationContext context = new StandardEvaluationContext(tesla);
String name = (String) exp.getValue(context);
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
String name = (String) exp.getValue(tesla);
PS:在spEL中,被取值的对象,被叫做根对象(root object)。通过EvaluationContext
接口可以设置其需要解析的根对象,也可以直接通过Object getValue(Object rootObject)
来指明需要获取的是哪个对象的值。
——参考《Spring Framework Reference Documentation 4.3.3.RELEASE》