Spring表达语言(SpEL)教程
Spring Expression Language(SpEL)是一种表达语言,支持在运行时查询和操作对象图。在本文中,我们将看到结合使用SpEL和基于XML和基于注释的配置的示例。
表达式语言支持以下功能:
- 文字表达
- 布尔,关系和数学运算符
- 常用表达
- 类表达式
- 访问属性,数组,列表和映射
- 方法调用
- 分配
- 调用构造函数
- Bean参考
- 内联列表
- 内联地图
- 三元运算符
Bean定义中的SpEL表达式
定义表达式的语法格式为#{<表达式字符串>}。例如,使用SpEL分配文字表达式。
如果使用XML配置
<bean id="address" class="com.theitroad.springproject.Address">
<property name="pinCode" value = "#{'110001'}" />
</bean>
如果使用@Value注释
@Component
public class Address {
...
...
@Value("#{'302001'}")
private String pinCode;
...
...
}
SpEL关系运算符
SpEL支持以下关系运算符。我们可以使用符号或者等效的字母(对于XML文档)
•lt(<)
•gt(>)
•le(<=)
•ge(> =)
•eq(==)
•ne(!=)
•不(!)
SpEL关系运算符
@Component
public class Values {
@Value("#{6 < 7}")
private boolean valueLessThan;
@Value("#{6 == 7}")
private boolean valueEqual;
@Value("#{6 == 7}")
private boolean valueNotEqual;
// Using alphabetic
@Value("#{6 ge 7}")
private boolean valueGreaterEqual;
public boolean isValueLessThan() {
return valueLessThan;
}
public boolean isValueEqual() {
return valueEqual;
}
public boolean isValueNotEqual() {
return valueNotEqual;
}
public boolean isValueGreaterEqual() {
return valueGreaterEqual;
}
}
输出:
isValueLessThan()-true isValueEqual()-false isValueNotEqual()-false isValueGreaterEqual()-false
SpEL逻辑运算符
SpEL支持以下逻辑运算符。我们可以使用符号或者等效字母。
和(&&)
或者(||)
不是(!)
SpEL逻辑运算符示例
@Component
public class Values {
@Value("#{6 < 7 && 7 < 8} ")
private boolean valueLessThan;
@Value("#{6 == 7 and 7 == 7}")
private boolean valueEqual;
@Value("#{6 != 7 || 8 != 8}")
private boolean valueNotEqual;
@Value("#{!(6 lt 7)}")
private boolean valueNotOperator;
public boolean isValueLessThan() {
return valueLessThan;
}
public boolean isValueEqual() {
return valueEqual;
}
public boolean isValueNotEqual() {
return valueNotEqual;
}
public boolean isValueNotOperator() {
return valueNotOperator;
}
}
输出:
isValueLessThan()-true isValueEqual()-false isValueNotEqual()-true isValueNotOperator()-false
SpEL数学运算符
SpEL支持以下数学运算符。
加号运算符(+)可以与数字和字符串一起使用。
减法运算符(-)
乘法运算符(*)
除法运算符(/)
模量(%)
指数幂(^)
减,乘和除运算符只能用于数字。
SpEL数学运算符示例
@Component
public class Values {
@Value("#{6 + 8} ")
private int valueAddition;
@Value("#{'Hello ' + 'World'}")
private String valueStringAddition;
@Value("#{17 div 5}")
private int valueDivision;
@Value("#{17 % 5}")
private int valueModDivision;
@Value("#{2^3}")
private double valuePower;
public int getValueAddition() {
return valueAddition;
}
public String getValueStringAddition() {
return valueStringAddition;
}
public int getValueDivision() {
return valueDivision;
}
public int getValueModDivision() {
return valueModDivision;
}
public double getValuePower() {
return valuePower;
}
}
输出:
valueAddition- 14 valueStringAddition- Hello World valueDivision- 3 valueModDivision- 2 valuePower- 8.0
SpEL Bean参考和方法调用示例
在此示例中,我们将看到如何引用bean以及如何使用Spring表达式语言在bean上调用方法。为此,我们将有两个类Person和Address,并且Person将具有Address Bean引用。
地址类别
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Address {
@Value("100")
private String houseNo;
@Value("The Mall")
private String street;
@Value("Shimla")
private String city;
@Value("HP")
private String state;
// As SpEL literal
@Value("#{'171004'}")
private String pinCode;
public String getHouseNo() {
return houseNo;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public String getPinCode() {
return pinCode;
}
public void setHouseNo(String houseNo) {
this.houseNo = houseNo;
}
public void setStreet(String street) {
this.street = street;
}
public void setCity(String city) {
this.city = city;
}
public void setState(String state) {
this.state = state;
}
public void setPinCode(String pinCode) {
this.pinCode = pinCode;
}
}
人类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Person {
@Value("#{'Suresh'}")
private String name;
@Value("34")
private int age;
// SpEL Bean reference
@Value("#{address}")
private Address address;
@Value("#{address.city}")
private String personCity;
// SpEL Method invocation
@Value("#{person.getInfo()}")
private String personInfo;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Address getAddress() {
return address;
}
public String getPersonCity() {
return personCity;
}
public String getPersonInfo() {
return personInfo;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(Address address) {
this.address = address;
}
public void setPersonCity(String personCity) {
this.personCity = personCity;
}
public void setPersonInfo(String personInfo) {
this.personInfo = personInfo;
}
public String getInfo(){
return getName() + " - Address " + getAddress().getHouseNo() +
", " + getAddress().getStreet() + ", " + getAddress().getCity()
+ ", " + getAddress().getState() + ", " + getAddress().getPinCode();
}
}
输出:
Person State HP Person City Shimla Person Info Suresh - Address 100, The Mall, Shimla, HP, 171004
可以按以下说明使用针对相同bean的SpEL表达式进行XML配置,请注意,其中一个区别是Address Bean中的getInfo()方法。
<?xml version="1.0" encoding="UTF-8"?>
<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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="address" class="com.theitroad.springexample.Address">
<property name="houseNo" value = "100" />
<property name="street" value = "The Mall" />
<property name="city" value = "Shimla" />
<property name="state" value = "HP" />
<property name="pinCode" value = "#{'171004'}" />
</bean>
<bean id="person" class="com.theitroad.springexample.Person">
<property name="name" value = "#{'Suresh'}" />
<!-- Bean reference through SpEL -->
<property name="address" value = "#{address}" />
<property name="personCity" value = "#{address.city}" />
<!-- SpEL Method invocation-->
<property name="personInfo" value = "#{address.getInfo()}" />
</bean>
</beans>
使用SpEL安全导航运算符(?。)
安全导航运算符用于避免NullPointerException。通常,当我们引用一个对象时,可能需要在访问该对象的方法或者属性之前验证其是否为null。如果对象引用为null,则安全导航运算符将返回null,而不是引发异常。
例如,存在一个具有以下属性的Address类。请注意,城市没有任何价值。
@Component
public class Address {
@Value("100")
private String houseNo;
@Value("The Mall")
private String street;
private String city;
@Value("HP")
private String state;
// As SpEL literal
@Value("#{'171004'}")
private String pinCode;
现在,我们尝试获取Person类的personCity属性中的city并将其更改为全部大写。
@Component
public class Person {
@Value("#{'Suresh'}")
private String name;
@Value("34")
private int age;
// SpEL Bean reference
@Value("#{address}")
private Address address;
@Value("#{address.city.toUpperCase()}")
private String personCity;
由于城市本身返回为null,因此此分配引发UnsatisfiedDependencyException。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person': Unsatisfied dependency expressed through field 'personCity'; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method toUpperCase() on null context object
为了避免出现异常,可以使用安全的导航运算符(?。),现在personCity本身返回为null。
@Value("#{address.city?.toUpperCase()}")
private String personCity;
SpEL三元和Elvis运算符
我们可以使用三元运算符在表达式内部执行if-then-else条件逻辑。
@Component
public class Values {
@Value("#{10} ")
private int value1;
@Value("#{20}")
private int value2;
@Value("#{30}")
private int value3;
public int getValue1() {
return value1;
}
public int getValue2() {
return value2;
}
public int getValue3() {
return value3;
}
public void setValue1(int value1) {
this.value1 = value1;
}
public void setValue2(int value2) {
this.value2 = value2;
}
public void setValue3(int value3) {
this.value3 = value3;
}
}
在ConditionOps中,属性ternaryValue的类值是使用SpEL三元运算符计算的。如果value1大于value3,则ternaryValue等于value1,否则等于value3.
@Component
public class ConditionOps {
@Value("#{values.value1 gt values.value3 ? values.value1 : values.value3 }")
private int ternaryValue;
public int getTernaryValue() {
return ternaryValue;
}
public void setTernaryValue(int ternaryValue) {
this.ternaryValue = ternaryValue;
}
}
SpEL Elvis运算符示例
Elvis运算符是三元运算符语法的缩写。使用三元运算符语法,通常在检查变量是否为null时必须重复两次该变量,然后如果不为null则返回该变量,如果为null则返回默认值。
@Value("#{values.name != null ? values.name : \"Unknown\"}")
private String displayName;
在这种情况下,我们可以使用Elvis运算符
@Value("#{values.name ?: \"Unknown\"}")
private String displayName;
带正则表达式的SpEL
SpEL使用匹配运算符和正则表达式。如果正则表达式匹配给定值,则matchs运算符返回true,否则返回false。
@Value("#{values.email matches '[a-zA-Z0-9._]Hyman@theitroad[a-zA-Z0-9]+\.com'}")
private boolean isEmail;

