Spring Expression Language(简称SpEL)是一种功能强大的表达式语言、 在Spring3中就已经支持EL表达式了, Spring Expression Language(SpEL)是类似于OGNL和JSF EL的表达式语言, 能够在运行时构建复杂表达式, 存取对象属性、调用
一、SpEL有三种用法,一种是在注解@Value中;一种是XML配置;最后一种是在代码块中使用Expression。
1. @Value
如果修饰成员变量,是从Spring容器中按照SpEL表达式筛选修改数据后,赋值给所修饰的变量;
//@Value能修饰成员变量和方法形参
//#{}内就是表达式的内容
@Value("#{表达式}")
public String arg;
如果修饰方法形参,则是过滤传进来的参数值。
@CacheEvict(value = DEMO_CACHE_NAME,key = "'user_'+#uuid")//这是清除缓存
public void delete(String uuid){
userDao.delete(uuid);
}
2. <bean>配置
<bean id="xxx" class="com.java.XXXXX.xx">
<!-- 同@Value,#{}内是表达式的值,可放在property或constructor-arg内 -->
<property name="arg" value="#{表达式}">
</bean>
用法跟注解@ Value修饰形参类似
3. Expression
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class SpELTest {
public static void main(String[] args) {
//创建ExpressionParser解析表达式
ExpressionParser parser = new SpelExpressionParser();
//表达式放置
Expression exp = parser.parseExpression("表达式");
//执行表达式,默认容器是spring本身的容器:ApplicationContext
Object value = exp.getValue();
/**如果使用其他的容器,则用下面的方法*/
//创建一个虚拟的容器EvaluationContext
StandardEvaluationContext ctx = new StandardEvaluationContext();
//向容器内添加bean
BeanA beanA = new BeanA();
ctx.setVariable("bean_id", beanA);
//setRootObject并非必须;一个EvaluationContext只能有一个RootObject,引用它的属性时,可以不加前缀
ctx.setRootObject(XXX);
//getValue有参数ctx,从新的容器中根据SpEL表达式获取所需的值
Object value = exp.getValue(ctx);
}
}
用法比较灵活,可以在代码中使用SpEL进行数据的过滤和修改 ,表达式写法网上有很多,详情可参考Spring表达式语言
所有的SpEL都支持XML和Annotation两种方式,格式:#{ SpEL expression },接下来重点讲解这两种的应用方式:
二、 第一个Spring EL例子—— HelloWorld Demo
这个例子将展示如何利用SpEL注入String、Integer、Bean到属性中。
1. Spring El的依赖包
首先在Maven的pom.xml中加入依赖包,这样会自动下载SpEL的依赖。
文件:pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
</dependencies>
2. Spring Bean
接下来写两个简单的Bean,稍后会用SpEL注入value到属性中。
Item.java如下:
package com.feng.demo.el;
public class Item {
private String name;
private int total;
//getter and setter...
}
Customer.java如下:
package com.feng.demo.el;
public class Customer {
private Item item;
private String itemName;
@Override
public String toString() {
return "itemName=" +this.itemName+" "+"Item.total="+this.item.getTotal();
}
//getter and setter...
}
3. Spring EL——XML
SpEL格式为#{ SpEL expression },xml配置见下。
文件:Spring-EL.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="itemBean" class="com.feng.demo.el.Item">
<property name="name" value="itemA" />
<property name="total" value="10" />
</bean>
<bean id="customerBean" class="com.feng.demo.el.Customer">
<property name="item" value="#{itemBean}" />
<property name="itemName" value="#{itemBean.name}" />
</bean>
</beans>
注解:
1. #{itemBean}——将itemBean注入到customerBean的item属性中。
2. #{itemBean.name}——将itemBean 的name属性,注入到customerBean的属性itemName中。
4. Spring EL——Annotation
SpEL的Annotation版本。
注意:要在Annotation中使用SpEL,必须要通过annotation注册组件。如果你在xml中注册了bean和在java class中定义了@Value,@Value在运行时将失败。
Item.java如下:
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("itemBean")
public class Item {
@Value("itemA")//直接注入String
private String name;
@Value("10")//直接注入integer
private int total;
//getter and setter...
}
Customer.java如下:
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("customerBean")
public class Customer {
@Value("#{itemBean}")
private Item item;
@Value("#{itemBean.name}")
private String itemName;
//getter and setter...
}
Xml中配置组件自动扫描
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.feng.demo.el" />
</beans>
在Annotation模式中,用@Value定义EL。在这种情况下,直接注入一个String和integer值到itemBean中,然后注入itemBean到customerBean中。
5. 输出结果
App.java如下:
package com.feng.demo.el;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-EL.xml");
Customer obj = (Customer) context.getBean("customerBean");
System.out.println(obj);
}
}
输出结果如下:itemName=itemA item.total=10
二、 Spring EL Method Invocation——SpEL 方法调用
SpEL允许开发者用El运行方法函数,并且允许将方法返回值注入到属性中。
1. Spring EL Method Invocation之Annotation
此段落演示用@Value注释,完成SpEL方法调用。
Customer.java如下:
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("customerBean")
public class Customer {
@Value("#{'lei'.toUpperCase()}")
private String name;
@Value("#{priceBean.getSpecialPrice()}")
private double amount;
//getter and setter...省略
@Override
public String toString() {
return "Customer [name=" + name + ", amount=" + amount + "]";
}
}
Price.java如下:
package com.feng.demo.el;
import org.springframework.stereotype.Component;
@Component("priceBean")
public class Price {
public double getSpecialPrice() {
return new Double(99.99);
}
}
输出结果:Customer[name=LEI,amount=99.99]
上例中,以下语句调用toUpperCase()方法
@Value("#{'lei'.toUpperCase()}")
private String name;
上例中,以下语句调用priceBean中的getSpecialPrice()方法
@Value("#{priceBean.getSpecialPrice()}")
private double amount;
2. Spring EL Method Invocation之XML
在XMl中配置如下,效果相同
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="customerBean" class="com.fengdemo.el.Customer">
<property name="name" value="#{'lei'.toUpperCase()}" />
<property name="amount" value="#{priceBean.getSpecialPrice()}" />
</bean>
<bean id="priceBean" class="com.feng.demo.el.Price" />
</beans>
3.其它常用操作
public class TestSpringEL {
/*
* TestConstant类中有两个方法重载,
* 返回值为String类型
*/
// 调用无参方法
@Value("#{testConstant.showProperty}")
private String method1;
// 有参接收字符串的方法
@Value("#{testConstant.showProperty('Hello')}")
private String method2;
/*
* 若然希望方法返回的String为大写
*/
@Value("#{testConstant.showProperty().toUpperCase()}")
private String method3;
/*
* 若使用method3这种方式,若然showProperty返回为null,
* 将会抛出NullPointerException,可以使用以下方式避免
*/
@Value("#{testConstant.showProperty()?.toUpperCase}")
private String method4;
/*
* 使用?.符号代表若然左边的值为null,将不执行右边方法,
* 读者可以灵活运用在其他场景,只要左边可能返回null,
* 即可使用上面示例中的?.
*/
}
三、 Spring EL Operators——SpEL 操作符
Spring EL 支持大多数的数学操作符、逻辑操作符、关系操作符。
1.关系操作符
包括:等于 (==, eq),不等于 (!=, ne),小于 (<, lt),,小于等于(<= , le),大于(>, gt),大于等于 (>=, ge)
2.逻辑操作符
包括:and,or,and not(!)
3.数学操作符
包括:加 (+),减 (-),乘 (*),除 (/),取模 (%),幂指数 (^)。
1. Spring EL Operators之Annotation
Numer.java如下
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("numberBean")
public class Number {
@Value("999")
private int no;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
}
Customer.java如下
package com.lei.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("customerBean")
public class Customer {
//Relational operators
@Value("#{1 == 1}") //true
private boolean testEqual;
@Value("#{1 != 1}") //false
private boolean testNotEqual;
@Value("#{1 < 1}") //false
private boolean testLessThan;
@Value("#{1 <= 1}") //true
private boolean testLessThanOrEqual;
@Value("#{1 > 1}") //false
private boolean testGreaterThan;
@Value("#{1 >= 1}") //true
private boolean testGreaterThanOrEqual;
//Logical operators , numberBean.no == 999
@Value("#{numberBean.no == 999 and numberBean.no < 900}") //false
private boolean testAnd;
@Value("#{numberBean.no == 999 or numberBean.no < 900}") //true
private boolean testOr;
@Value("#{!(numberBean.no == 999)}") //false
private boolean testNot;
//Mathematical operators
@Value("#{1 + 1}") //2.0
private double testAdd;
@Value("#{'1' + '@' + '1'}") //1@1
private String testAddString;
@Value("#{1 - 1}") //0.0
private double testSubtraction;
@Value("#{1 * 1}") //1.0
private double testMultiplication;
@Value("#{10 / 2}") //5.0
private double testDivision;
@Value("#{10 % 10}") //0.0
private double testModulus ;
@Value("#{2 ^ 2}") //4.0
private double testExponentialPower;
@Override
public String toString() {
return "Customer [testEqual=" + testEqual + ", testNotEqual="
+ testNotEqual + ", testLessThan=" + testLessThan
+ ", testLessThanOrEqual=" + testLessThanOrEqual
+ ", testGreaterThan=" + testGreaterThan
+ ", testGreaterThanOrEqual=" + testGreaterThanOrEqual
+ ", testAnd=" + testAnd + ", testOr=" + testOr + ", testNot="
+ testNot + ", testAdd=" + testAdd + ", testAddString="
+ testAddString + ", testSubtraction=" + testSubtraction
+ ", testMultiplication=" + testMultiplication
+ ", testDivision=" + testDivision + ", testModulus="
+ testModulus + ", testExponentialPower="
+ testExponentialPower + "]";
}
}
运行如下代码:
Customer obj = (Customer) context.getBean("customerBean");
System.out.println(obj);
结果如下:
Customer [
testEqual=true,
testNotEqual=false,
testLessThan=false,
testLessThanOrEqual=true,
testGreaterThan=false,
testGreaterThanOrEqual=true,
testAnd=false,
testOr=true,
testNot=false,
testAdd=2.0,
testAddString=1@1,
testSubtraction=0.0,
testMultiplication=1.0,
testDivision=5.0,
testModulus=0.0,
testExponentialPower=4.0
]
2. Spring EL Operators之XML
以下是等同的xml配置。
注意,类似小于号“<”,或者小于等于“<=”,在xml中是不直接支持的,必须用等同的文本表示方法表示,
例如,“<”用“lt”替换;“<=”用“le”替换,等等。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="customerBean" class="com.feng.demo.el.Customer">
<property name="testEqual" value="#{1 == 1}" />
<property name="testNotEqual" value="#{1 != 1}" />
<property name="testLessThan" value="#{1 lt 1}" />
<property name="testLessThanOrEqual" value="#{1 le 1}" />
<property name="testGreaterThan" value="#{1 > 1}" />
<property name="testGreaterThanOrEqual" value="#{1 >= 1}" />
<property name="testAnd" value="#{numberBean.no == 999 and numberBean.no lt 900}" />
<property name="testOr" value="#{numberBean.no == 999 or numberBean.no lt 900}" />
<property name="testNot" value="#{!(numberBean.no == 999)}" />
<property name="testAdd" value="#{1 + 1}" />
<property name="testAddString" value="#{'1' + '@' + '1'}" />
<property name="testSubtraction" value="#{1 - 1}" />
<property name="testMultiplication" value="#{1 * 1}" />
<property name="testDivision" value="#{10 / 2}" />
<property name="testModulus" value="#{10 % 10}" />
<property name="testExponentialPower" value="#{2 ^ 2}" />
</bean>
<bean id="numberBean" class="com.feng.demo.el.Number">
<property name="no" value="999" />
</bean>
</beans>
四、 Spring EL 三目操作符condition?true:false
SpEL支持三目运算符,以此来实现条件语句。
1. Annotation
Item.java如下:
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("itemBean")
public class Item {
@Value("99")
private int qtyOnHand;
public int getQtyOnHand() {
return qtyOnHand;
}
public void setQtyOnHand(int qtyOnHand) {
this.qtyOnHand = qtyOnHand;
}
}
Customer.java如下:
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("customerBean")
public class Customer {
@Value("#{itemBean.qtyOnHand < 100 ? true : false}")
private boolean warning;
public boolean isWarning() {
return warning;
}
public void setWarning(boolean warning) {
this.warning = warning;
}
@Override
public String toString() {
return "Customer [warning=" + warning + "]";
}
}
输出:Customer [warning=true]
2. XMl
Xml配置如下,注意:应该用“<;”代替小于号“<”
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="customerBean" class="com.feng.demo.el.Customer">
<property name="warning"
value="#{itemBean.qtyOnHand < 100 ? true : false}" />
</bean>
<bean id="itemBean" class="com.feng.demo.el.Item">
<property name="qtyOnHand" value="99" />
</bean>
</beans>
输出:Customer [warning=true]
五、 Spring EL 操作List、Map集合取值
此段演示SpEL怎样从List、Map集合中取值,简单示例如下:
//get map where key = 'MapA'
@Value("#{testBean.map['MapA']}")
private String mapA;
//get first value from list, list is 0-based.
@Value("#{testBean.list[0]}")
private String list;
1. Annotation
首先,创建一个HashMap和ArrayList,并初始化一些值。
Test.java如下:
package com.feng.demo.el;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Component;
@Component("testBean")
public class Test {
private Map<String, String> map;
private List<String> list;
public Test() {
map = new HashMap<String, String>();
map.put("MapA", "This is A");
map.put("MapB", "This is B");
map.put("MapC", "This is C");
list = new ArrayList<String>();
list.add("List0");
list.add("List1");
list.add("List2");
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
然后,用SpEL取值,Customer.java如下
package com.feng.demo.el;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("customerBean")
public class Customer {
@Value("#{testBean.map['MapA']}")
private String mapA;
@Value("#{testBean.list[0]}")
private String list;
public String getMapA() {
return mapA;
}
public void setMapA(String mapA) {
this.mapA = mapA;
}
public String getList() {
return list;
}
public void setList(String list) {
this.list = list;
}
@Override
public String toString() {
return "Customer [mapA=" + mapA + ", list=" + list + "]";
}
}
调用代码如下:
Customer obj = (Customer) context.getBean("customerBean");
System.out.println(obj);
输出结果:Customer [mapA=This is A, list=List0]
2. XML
Xml配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="customerBean" class="com.feng.demo.el.Customer">
<property name="mapA" value="#{testBean.map['MapA']}" />
<property name="list" value="#{testBean.list[0]}" />
</bean>
<bean id="testBean" class="com.feng.demo.el.Test" />
</beans>
六、SpringEL调用静态类或常量
public class TestSpringEL {
/*
* 注入JDK中的工具类常量或调用工具类的方法
*/
// 获取Math的PI常量
@Value("#{T(java.lang.Math).PI")
private double pi;
// 调用random方法获取返回值
@Value("#{T(java.lang.Math).random()}")
private double ramdom;
// 获取文件路径符号
@Value("#{T(java.io.File).separator}")
private String separator;
}
七、Spring操作外部Properties文件
<!-- 首先通过applicaContext.xml中<util:properties>增加properties文件 -->
<!-- 注意需要引入Spring的util schemea命名空间和注意id属性,id属性将在SpringEL中使用 -->
<util:properties id="test" location="classpath:application.properties"/>
public class TestSpringEL {
// 注意test为xml文件中声明的id
@Value("#{test['jdbc.url']}")
private String propertiesValue;
}