Spring EL 简介

本文参考自Spring官方文档 Spring EL。

在Java上有很多表达式语言,在很多领域有各种各样的应用。我们应该很熟悉Java EE的表达式语言吧,让我们能在JSP中随意插入数据。Spring也提供了一个表达式语言并添加了自己的功能,以便可以方便的和各种Spring框架交互。我们在项目中不需要手动管理Spring表达式的这些接口和实例,只需要在合适的时候编写Spring表达式,转换器就会自动解析并转换表达式。

创建和使用解析器

当然,为了说明一下Spring表达式,我们在这里还是手动创建一个解析器来解析表达式。下面是简单的单元测试。

public class SpringElTest {
    private static ExpressionParser parser = new SpelExpressionParser();

    @Test
    public void testHelloWorld() {
        Expression expression = parser.parseExpression("'你好世界!'");
        String result = (String) expression.getValue();
        System.out.println(result);

    }
}

还可以使用更复杂的例子。

    @Test
    public void testStringOperation() {
        Expression expression = parser.parseExpression("'你好'.concat('世界!')");
        String result = (String) expression.getValue();
        System.out.println(result);
        expression = parser.parseExpression("'Hello world!'.toUpperCase()");
        result = expression.getValue(String.class);
        System.out.println(result);
    }

Spring文档解释了如何创建和使用Spring表达式的各个接口、编译和配置等等。但是一般情况下我们用不到这些功能。这里就只介绍一下Spring El的语法。如果需要详细了解这些信息的话还是直接看文档吧。

语言指南

这部分介绍了Spring EL表达式的使用。为了省事就直接引用了文档的代码了。下面这些代码没有说明的话都是Spring文档的例子。

字面值

表达式支持各种类型的字面值。字符串字面值需要使用单引号包括,其他类型字面值直接写就行。

ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

属性和集合

Spring表达式支持属性,只要使用点号引用属性即可。

int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);

String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);

数组和列表可以使用方括号语法引用对应索引的元素。

ExpressionParser parser = new SpelExpressionParser();

// Inventions Array
StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla);

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
        teslaContext, String.class);

// Members List
StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee);

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(
        societyContext, String.class);

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

Map类型也可以使用方括号语法引用键对应的值。

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");

内联集合

我们可以直接在表达式中定义集合,这就是内联。内联集合使用花括号语法。

List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);

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

内联Map则要复杂一点,使用类似JSON的语法,键和值之间用冒号分隔开。

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);

数组

数组使用类似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);

方法

方法和Java语法一样。

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

运算符

表达式中支持各种运算符,运算规则和Java规则类似。唯一需要注意的是空值的处理,假设有非空值val,那么下面的表达式恒为真:val > null。这一点需要注意。

// 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);

逻辑运算符可以使用andor!

类型

特殊的T运算符可以获取表达式对象的类型。

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);

构造器

在表达式中,使用new关键字来调用构造器。

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

变量

在表达式上下文中,我们可以设置新变量。然后在表达式中使用#变量名访问变量。

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就是当前的表达式上下文对象,#this则根据当前求值环境的不同而变化。下面的例子中,#this即每次循环的值。

// create an array of integers
List primes = new ArrayList();
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 primesGreaterThanTen = (List) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);

Bean引用

这是Spring表达式独有的功能,我们可以在表达式中引用配置文件定义的其他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);

如果需要获取Bean工厂本身而不是它构造的Bean,可以使用&Bean名称

Object bean = parser.parseExpression("&foo").getValue(context);

三元运算符

和Java的三元运算符类似。

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

Elvis运算符

在一些编程语言中(比如C#、Kotlin等)提供该功能,语法是?:。意义是当某变量不为空的时候使用该变量,当该变量为空的时候使用指定的默认值。

ExpressionParser parser = new SpelExpressionParser();

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

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

安全导航运算符

这是来自Groovy的一个功能,语法是?.,当然有些语言也提供了这个功能。当我们对对象的某个属性求值时,如果该对象本身为空,就会抛出空指针异常,如果使用安全导航运算符,空对象的属性就会简单的返回空。

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

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

集合选择

这有点类似Java 8的Filter流方法,作用是选择或者说是过滤,语法是集合对象.?[选择表达式],Spring会迭代集合对象的每一个元素,并使用选择表达式判断该元素是否满足条件,最后返回由满足条件的元素组成的新集合。下面的例子就返回了值大于27的新Map。

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

集合投影

这类似Java 8的Map流方法或者SQL语言的选择语句,作用是将一个集合中所有元素的某属性抽取出来,组成一个新集合。语法是![投影表达式]。下面的例子选出了由Member的placeOfBirth的city属性组成的新集合。

List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");

表达式模板

表达式模板使用#{}定义,它允许我们混合多种结果。下面就是一个例子,首先Spring会先对模板中的表达式求值,在这里是返回一个随机值,然后将结果和外部的表达式组合起来。最终的结果就向下面这样了。

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

如果表达式只是一个简单的表达式,就不需要使用模板。只有表达式有很多表达式组成时才需要。

你可能感兴趣的:(Spring EL 简介)