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;