Spring表达语言(SpEL)教程

时间:2020-01-09 10:44:34  来源:igfitidea点击:

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;