Spring 表达式语言 (SpEL)



// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(
        societyContext, String.class);

maps的内容由内指定的key值获得. 在这种情况下,因为对于人员的键映射是字符串,我们可以指定 字符串。

// Officer's Dictionary

Inventor pupin = parser.parseExpression("Officers['president']").getValue(
        societyContext, Inventor.class);

// evaluates to "Idvor"
String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(
        societyContext, String.class);

// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
        societyContext, "Croatia");

8.5.3 内联列表

列表可以直接在表达式中使用{}符号来表示。

// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);

List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);

{}本身意味着空列表。出于性能的考虑,如果列表本身 完全由固定面值的则恒定列表被创建以代表 表达,而不是建立一个新的列表上的每个都执行。

8.5.4 内联maps

{key:value}记号的maps也可以使用表达式直接表示。

// evaluates to a Java map containing the two entries
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);

Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);

{:}本身意味着一个空映射。出于性能的考虑,如果map本身是由 固定的文字或其他嵌套结构不变的(list或者map),那么一个恒定的map创建 代表表达,而不是建立新map每次去执行。使用.引用map的key 是可选的,上面的例子并没有使用.引用key。

8.5.5 array构造

array可以使用熟悉的Java语法,选择性地提供一个初始建立 有在构造时的数组。

int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);

// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);

// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

即未被初始化的多维数组也可以被构造。

8.5.6 方法

方法被调用通过java典型的编程语法实现,您也可以调用方法 在表示式当中。可变参数也支持。

// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);

// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
        societyContext, Boolean.class);

8.5.7 运算符

关系运算符

关系运算符;等于,不等于,小于,小于或等于,大于, 和大于或等于使用标准算子表示法的支持。

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

除了标准的关系运算符SpEL支持instanceof和 增则表达式的matches操作。

// evaluates to false
boolean falseValue = parser.parseExpression(
        "'xyz' instanceof T(int)").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression(
        "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

//evaluates to false
boolean falseValue = parser.parseExpression(
        "'5.0067' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

每个符号操作者也可以被指定为一个纯字母变量。这个 避免了在使用的符号有特殊含义的文档类型的问题 其表达被嵌入(例如,XML文档)。文本是等值 比如: lt (<),gt (>), le (<=), ge (>=), eq (==), ne (!=), div (/), mod (%), not (!). 这些都是不区分大小写。

逻辑运算符

所以支持的逻辑运算符 and, or, and not. 下文将证明他们的使用

// -- AND --

// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);

// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
数学运​​算符

加法运算符可以用于数字和字符串。减法,乘法 和除法只能在数字被使用。支持其他数学运算符 模量(%)和指数幂(^)。标准的运算符优先级执行。 这些 运算符展示在下文。

// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2

String testString = parser.parseExpression(
        "'test' + ' ' + 'string'").getValue(String.class); // test string

// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000

// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0

// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0

// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3

int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1

// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21

8.5.8 赋值

设置一个属性是通过使用赋值操作完成。这通常是 调用setValue,但也可以在调用getValue内完成。

Inventor inventor = new Inventor();
StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);

parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");

// alternatively

String aleks = parser.parseExpression(
        "Name = 'Alexandar Seovic'").getValue(inventorContext, String.class);

8.5.9 类型

T操作可以被用来指定安装一个java.lang.ClassClass (the type). 静态方法也可以使用该运算符调用。然后该 StandardEvaluationContext使用TypeLocator找到类型和 StandardTypeLocator(可替换)是建立与的理解 java.lang package. 这意味着T()引用内的java.lang类型不需要是 完全限定,但所有其他类型的引用必须。

Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);

Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);

boolean trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean.class);

8.5.10 构造

构造函数可以使用new运算符调用。所有地方的类名应该是符合要求的, ,除了原语类型和字符串(其中整数,浮点,等等,都可以 使用)。

Inventor einstein = p.parseExpression(
        "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor.class);

//create new inventor instance within add method of List
p.parseExpression(
        "Members.add(new org.spring.samples.spel.inventor.Inventor(
            'Albert Einstein', 'German'))").getValue(societyContext);

8.5.11 变量

变量可以在使用语法#variableName表达引用。变量 使用在StandardEvaluationContext方法的setVariable设置。

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("Name = #newName").getValue(context);

System.out.println(tesla.getName()) // "Mike Tesla"
该#this 和 #root变量

变量#this 始终定义和指向的是当前的执行对象 (不支持对其中不合格的引用解析)。变量#root总是 定义和指向root context object。虽然#this可能作为表达式的一些组件被执行 ,但#root总是指 root。

// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

// create parser and set variable primes as the array of integers
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("primes",primes);

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);

8.5.12 函数

您可以通过注册,可以在该调用用户自定义函数扩展SpEL 表达式字符串。该函数注册到'StandardEvaluationContext`使用 该方法。

public void registerFunction(String name, Method m)

引用一个Java方法提供了函数的实现。举个例子 一个实用的方法来扭转字符串如下所示。

public abstract class StringUtils {

    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder();
        for (int i = 0; i < input.length(); i++)
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }
}

这个方法在解析器上线文当中被注册,作为字符串被调用。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

context.registerFunction("reverseString",
    StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class }));

String helloWorldReversed = parser.parseExpression(
    "#reverseString('hello')").getValue(context, String.class);

8.5.13 bean引用

如果解析上下文已经配置,那么bean解析器能够 从表达式使用(@)符号查找bean类。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);

8.5.14 三元运算符(IF-THEN-ELSE)

您可以使用三元运算符内部执行的if-then-else条件逻辑 的表达。一个最简单的例子是:

String falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String.class);

在这种情况下,在返回字符串值“falseExp'布尔假的结果。 更多案例:

parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");

expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
        "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";

String queryResultString = parser.parseExpression(expression)
        .getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"

同时可以在下一节看到 Elvis 运算符 使用一个更短的三元运算符语法。

8.5.15 Elvis操作符

Elvis操作符是三元运算符语法的缩短,并用于在 Groovy语言。 与三元运算符的语法,你通常要重复变量两次, 示例:

String name = "Elvis Presley";
String displayName = name != null ? name : "Unknown";

取而代之,你可以使用Elvis操作符,命名灵感来自猫王的发型风格。

ExpressionParser parser = new SpelExpressionParser();

String name = parser.parseExpression("null?:'Unknown'").getValue(String.class);

System.out.println(name); // Unknown

这里是一个更复杂的例子。

ExpressionParser parser = new SpelExpressionParser();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);

String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);

System.out.println(name); // Nikola Tesla

tesla.setName(null);

name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);

System.out.println(name); // Elvis Presley

8.5.16 安全导航运算符

安全导航操作符是用来避免'NullPointerException`和来自 该http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator(%3F.)[Groovy] 语言。通常情况下,当你有一个参考的对象,你可能需要验证 它不是访问方法或对象的属性之前空。为了避免这种情况,该 安全航行运算符将简单地返回空代替抛出的异常。

ExpressionParser parser = new SpelExpressionParser();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

StandardEvaluationContext context = new StandardEvaluationContext(tesla);

String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
System.out.println(city); // Smiljan

tesla.setPlaceOfBirth(null);

city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);

System.out.println(city); // null - does not throw NullPointerException!!!

[注意]

Elvis操作符可用于应用中的表达式的默认值,例如在一个 @ Value表达式:

@Value("#{systemProperties['pop3.port'] ?: 25}")

如果它不存在,那么将定义为25

8.5.17 集合选择

选择是一个强大的表达式语言功能,他允许你转换一些 源集合到另一个通过其条目选择。

选择使用语法?[selectionExpression].这将过滤收集和 返回一个包含原有元素的子集的新的集合。例如, 选择使我们能够轻松地获得Serbian inventors的列表:

List<Inventor> list = (List<Inventor>) parser.parseExpression(
        "Members.?[Nationality == 'Serbian']").getValue(societyContext);

选择可以被使用在lists或者maps集合当中。在前者的情况下,选择 标准执行对每个列表元素,同时针对map 选择定义的操作将会对map中的每个key执行。 (对象类似‘Map.Entry’) Map entries have their key and value accessible as properties for use in the selection。

下面这个例子将返回由原始映射(其中条目值小于27)所取得的这些元素的新map。

Map newMap = parser.parseExpression("map.?[value<27]").getValue();

除了返回所有选定的元素,也可以用来获取 第一或最后一个值。以获得第一条目相匹配的选择的语法是 ^ [...]而获得最后一个匹配选择语法是$ [...].

8.5.18 集合投影

投影允许集合驱动子表达式和解析 生成一个新的集合。语法投影![projectionExpression]. 多数 功能通过实例容易理解,假设我们有Inventor list,希望得到 他们出生的城市。有效的方式是我们要使用“placeOfBirth.city”解析 在Inventor 中的每个条目。使用投影:

// returns [Smiljan, Idvor ]
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");

一个map也可以用于驱动投影。 在这种情况下,投影表达式 将解析map中的每一个元素(作为Java Map.Entry方法的一个代理)。 通过投影一个map将获得一个由投影表达式遍历每个元素所得到的list。

8.5.19 表达模板

表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符, 当然,常见的选择是使用#{}作为分隔符。例如,

String randomPhrase = parser.parseExpression(
        "random number is #{T(java.lang.Math).random()}",
        new TemplateParserContext()).getValue(String.class);

// evaluates to "random number is 0.7038186818312008"

该字符串是通过连接文字“random number is”与 计算表达式的#{}定界符获取的结果,在此情况下的结果 中调用一个随机()方法。第二个参数的方法parseExpression() 是类型ParserContext的。在ParserContext接口用于影响如何 表达被解析,以便支持所述表达模板的功能。的TemplateParserContext的定义如下所示。

public class TemplateParserContext implements ParserContext {

    public String getExpressionPrefix() {
        return "#{";
    }

    public String getExpressionSuffix() {
        return "}";
    }

    public boolean isTemplate() {
        return true;
    }
}

8.6 Classes used in the examples

Inventor.java

package org.spring.samples.spel.inventor;

import java.util.Date;
import java.util.GregorianCalendar;

public class Inventor {

    private String name;
    private String nationality;
    private String[] inventions;
    private Date birthdate;
    private PlaceOfBirth placeOfBirth;

    public Inventor(String name, String nationality) {
        GregorianCalendar c= new GregorianCalendar();
        this.name = name;
        this.nationality = nationality;
        this.birthdate = c.getTime();
    }

    public Inventor(String name, Date birthdate, String nationality) {
        this.name = name;
        this.nationality = nationality;
        this.birthdate = birthdate;
    }

    public Inventor() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public Date getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }

    public PlaceOfBirth getPlaceOfBirth() {
        return placeOfBirth;
    }

    public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
        this.placeOfBirth = placeOfBirth;
    }

    public void setInventions(String[] inventions) {
        this.inventions = inventions;
    }

    public String[] getInventions() {
        return inventions;
    }
}

PlaceOfBirth.java

package org.spring.samples.spel.inventor;

public class PlaceOfBirth {

    private String city;
    private String country;

    public PlaceOfBirth(String city) {
        this.city=city;
    }

    public PlaceOfBirth(String city, String country) {
        this(city);
        this.country = country;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String s) {
        this.city = s;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

}

Society.java

package org.spring.samples.spel.inventor;

import java.util.*;

public class Society {

    private String name;

    public static String Advisors = "advisors";
    public static String President = "president";

    private List<Inventor> members = new ArrayList<Inventor>();
    private Map officers = new HashMap();

    public List getMembers() {
        return members;
    }

    public Map getOfficers() {
        return officers;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isMember(String name) {
        for (Inventor inventor : members) {
            if (inventor.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }

}
// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(
        societyContext, String.class);

maps的内容由内指定的key值获得. 在这种情况下,因为对于人员的键映射是字符串,我们可以指定 字符串。

// Officer's Dictionary

Inventor pupin = parser.parseExpression("Officers['president']").getValue(
        societyContext, Inventor.class);

// evaluates to "Idvor"
String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(
        societyContext, String.class);

// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
        societyContext, "Croatia");

8.5.3 内联列表

列表可以直接在表达式中使用{}符号来表示。

// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);

List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);

{}本身意味着空列表。出于性能的考虑,如果列表本身 完全由固定面值的则恒定列表被创建以代表 表达,而不是建立一个新的列表上的每个都执行。

8.5.4 内联maps

{key:value}记号的maps也可以使用表达式直接表示。

// evaluates to a Java map containing the two entries
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);

Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);

{:}本身意味着一个空映射。出于性能的考虑,如果map本身是由 固定的文字或其他嵌套结构不变的(list或者map),那么一个恒定的map创建 代表表达,而不是建立新map每次去执行。使用.引用map的key 是可选的,上面的例子并没有使用.引用key。

8.5.5 array构造

array可以使用熟悉的Java语法,选择性地提供一个初始建立 有在构造时的数组。

int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);

// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);

// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

即未被初始化的多维数组也可以被构造。

8.5.6 方法

方法被调用通过java典型的编程语法实现,您也可以调用方法 在表示式当中。可变参数也支持。

// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);

// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
        societyContext, Boolean.class);

8.5.7 运算符

关系运算符

关系运算符;等于,不等于,小于,小于或等于,大于, 和大于或等于使用标准算子表示法的支持。

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

除了标准的关系运算符SpEL支持instanceof和 增则表达式的matches操作。

// evaluates to false
boolean falseValue = parser.parseExpression(
        "'xyz' instanceof T(int)").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression(
        "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

//evaluates to false
boolean falseValue = parser.parseExpression(
        "'5.0067' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

每个符号操作者也可以被指定为一个纯字母变量。这个 避免了在使用的符号有特殊含义的文档类型的问题 其表达被嵌入(例如,XML文档)。文本是等值 比如: lt (<),gt (>), le (<=), ge (>=), eq (==), ne (!=), div (/), mod (%), not (!). 这些都是不区分大小写。

逻辑运算符

所以支持的逻辑运算符 and, or, and not. 下文将证明他们的使用

// -- AND --

// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);

// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
数学运​​算符

加法运算符可以用于数字和字符串。减法,乘法 和除法只能在数字被使用。支持其他数学运算符 模量(%)和指数幂(^)。标准的运算符优先级执行。 这些 运算符展示在下文。

// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2

String testString = parser.parseExpression(
        "'test' + ' ' + 'string'").getValue(String.class); // test string

// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000

// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0

// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0

// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3

int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1

// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21

8.5.8 赋值

设置一个属性是通过使用赋值操作完成。这通常是 调用setValue,但也可以在调用getValue内完成。

Inventor inventor = new Inventor();
StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);

parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");

// alternatively

String aleks = parser.parseExpression(
        "Name = 'Alexandar Seovic'").getValue(inventorContext, String.class);

8.5.9 类型

T操作可以被用来指定安装一个java.lang.ClassClass (the type). 静态方法也可以使用该运算符调用。然后该 StandardEvaluationContext使用TypeLocator找到类型和 StandardTypeLocator(可替换)是建立与的理解 java.lang package. 这意味着T()引用内的java.lang类型不需要是 完全限定,但所有其他类型的引用必须。

Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);

Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);

boolean trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean.class);

8.5.10 构造

构造函数可以使用new运算符调用。所有地方的类名应该是符合要求的, ,除了原语类型和字符串(其中整数,浮点,等等,都可以 使用)。

Inventor einstein = p.parseExpression(
        "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor.class);

//create new inventor instance within add method of List
p.parseExpression(
        "Members.add(new org.spring.samples.spel.inventor.Inventor(
            'Albert Einstein', 'German'))").getValue(societyContext);

8.5.11 变量

变量可以在使用语法#variableName表达引用。变量 使用在StandardEvaluationContext方法的setVariable设置。

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("Name = #newName").getValue(context);

System.out.println(tesla.getName()) // "Mike Tesla"
该#this 和 #root变量

变量#this 始终定义和指向的是当前的执行对象 (不支持对其中不合格的引用解析)。变量#root总是 定义和指向root context object。虽然#this可能作为表达式的一些组件被执行 ,但#root总是指 root。

// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

// create parser and set variable primes as the array of integers
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("primes",primes);

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);

8.5.12 函数

您可以通过注册,可以在该调用用户自定义函数扩展SpEL 表达式字符串。该函数注册到'StandardEvaluationContext`使用 该方法。

public void registerFunction(String name, Method m)

引用一个Java方法提供了函数的实现。举个例子 一个实用的方法来扭转字符串如下所示。

public abstract class StringUtils {

    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder();
        for (int i = 0; i < input.length(); i++)
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }
}

这个方法在解析器上线文当中被注册,作为字符串被调用。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

context.registerFunction("reverseString",
    StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class }));

String helloWorldReversed = parser.parseExpression(
    "#reverseString('hello')").getValue(context, String.class);

8.5.13 bean引用

如果解析上下文已经配置,那么bean解析器能够 从表达式使用(@)符号查找bean类。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);

8.5.14 三元运算符(IF-THEN-ELSE)

您可以使用三元运算符内部执行的if-then-else条件逻辑 的表达。一个最简单的例子是:

String falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String.class);

在这种情况下,在返回字符串值“falseExp'布尔假的结果。 更多案例:

parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");

expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
        "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";

String queryResultString = parser.parseExpression(expression)
        .getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"

同时可以在下一节看到 Elvis 运算符 使用一个更短的三元运算符语法。

8.5.15 Elvis操作符

Elvis操作符是三元运算符语法的缩短,并用于在 Groovy语言。 与三元运算符的语法,你通常要重复变量两次, 示例:

String name = "Elvis Presley";
String displayName = name != null ? name : "Unknown";

取而代之,你可以使用Elvis操作符,命名灵感来自猫王的发型风格。

ExpressionParser parser = new SpelExpressionParser();

String name = parser.parseExpression("null?:'Unknown'").getValue(String.class);

System.out.println(name); // Unknown

这里是一个更复杂的例子。

ExpressionParser parser = new SpelExpressionParser();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);

String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);

System.out.println(name); // Nikola Tesla

tesla.setName(null);

name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);

System.out.println(name); // Elvis Presley

8.5.16 安全导航运算符

安全导航操作符是用来避免'NullPointerException`和来自 该http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator(%3F.)[Groovy] 语言。通常情况下,当你有一个参考的对象,你可能需要验证 它不是访问方法或对象的属性之前空。为了避免这种情况,该 安全航行运算符将简单地返回空代替抛出的异常。

ExpressionParser parser = new SpelExpressionParser();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

StandardEvaluationContext context = new StandardEvaluationContext(tesla);

String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
System.out.println(city); // Smiljan

tesla.setPlaceOfBirth(null);

city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);

System.out.println(city); // null - does not throw NullPointerException!!!

[注意]

Elvis操作符可用于应用中的表达式的默认值,例如在一个 @ Value表达式:

@Value("#{systemProperties['pop3.port'] ?: 25}")

如果它不存在,那么将定义为25

8.5.17 集合选择

选择是一个强大的表达式语言功能,他允许你转换一些 源集合到另一个通过其条目选择。

选择使用语法?[selectionExpression].这将过滤收集和 返回一个包含原有元素的子集的新的集合。例如, 选择使我们能够轻松地获得Serbian inventors的列表:

List<Inventor> list = (List<Inventor>) parser.parseExpression(
        "Members.?[Nationality == 'Serbian']").getValue(societyContext);

选择可以被使用在lists或者maps集合当中。在前者的情况下,选择 标准执行对每个列表元素,同时针对map 选择定义的操作将会对map中的每个key执行。 (对象类似‘Map.Entry’) Map entries have their key and value accessible as properties for use in the selection。

下面这个例子将返回由原始映射(其中条目值小于27)所取得的这些元素的新map。

Map newMap = parser.parseExpression("map.?[value<27]").getValue();

除了返回所有选定的元素,也可以用来获取 第一或最后一个值。以获得第一条目相匹配的选择的语法是 ^ [...]而获得最后一个匹配选择语法是$ [...].

8.5.18 集合投影

投影允许集合驱动子表达式和解析 生成一个新的集合。语法投影![projectionExpression]. 多数 功能通过实例容易理解,假设我们有Inventor list,希望得到 他们出生的城市。有效的方式是我们要使用“placeOfBirth.city”解析 在Inventor 中的每个条目。使用投影:

// returns [Smiljan, Idvor ]
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");

一个map也可以用于驱动投影。 在这种情况下,投影表达式 将解析map中的每一个元素(作为Java Map.Entry方法的一个代理)。 通过投影一个map将获得一个由投影表达式遍历每个元素所得到的list。

8.5.19 表达模板

表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符, 当然,常见的选择是使用#{}作为分隔符。例如,

String randomPhrase = parser.parseExpression(
        "random number is #{T(java.lang.Math).random()}",
        new TemplateParserContext()).getValue(String.class);

// evaluates to "random number is 0.7038186818312008"

该字符串是通过连接文字“random number is”与 计算表达式的#{}定界符获取的结果,在此情况下的结果 中调用一个随机()方法。第二个参数的方法parseExpression() 是类型ParserContext的。在ParserContext接口用于影响如何 表达被解析,以便支持所述表达模板的功能。的TemplateParserContext的定义如下所示。

public class TemplateParserContext implements ParserContext {

    public String getExpressionPrefix() {
        return "#{";
    }

    public String getExpressionSuffix() {
        return "}";
    }

    public boolean isTemplate() {
        return true;
    }
}

8.6 Classes used in the examples

Inventor.java

package org.spring.samples.spel.inventor;

import java.util.Date;
import java.util.GregorianCalendar;

public class Inventor {

    private String name;
    private String nationality;
    private String[] inventions;
    private Date birthdate;
    private PlaceOfBirth placeOfBirth;

    public Inventor(String name, String nationality) {
        GregorianCalendar c= new GregorianCalendar();
        this.name = name;
        this.nationality = nationality;
        this.birthdate = c.getTime();
    }

    public Inventor(String name, Date birthdate, String nationality) {
        this.name = name;
        this.nationality = nationality;
        this.birthdate = birthdate;
    }

    public Inventor() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public Date getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }

    public PlaceOfBirth getPlaceOfBirth() {
        return placeOfBirth;
    }

    public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
        this.placeOfBirth = placeOfBirth;
    }

    public void setInventions(String[] inventions) {
        this.inventions = inventions;
    }

    public String[] getInventions() {
        return inventions;
    }
}

PlaceOfBirth.java

package org.spring.samples.spel.inventor;

public class PlaceOfBirth {

    private String city;
    private String country;

    public PlaceOfBirth(String city) {
        this.city=city;
    }

    public PlaceOfBirth(String city, String country) {
        this(city);
        this.country = country;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String s) {
        this.city = s;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

}

Society.java

package org.spring.samples.spel.inventor;

import java.util.*;

public class Society {

    private String name;

    public static String Advisors = "advisors";
    public static String President = "president";

    private List<Inventor> members = new ArrayList<Inventor>();
    private Map officers = new HashMap();

    public List getMembers() {
        return members;
    }

    public Map getOfficers() {
        return officers;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isMember(String name) {
        for (Inventor inventor : members) {
            if (inventor.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值