题外话

官方文档用evaluate这个单词来描述从表达式中获得实际内容的过程。如果直译的话,应该是评估、估值之类的意思。个人以为翻译成解析更易懂,但parse已经是解析了,为了避免冲突,就只好保留了evaluate的形式。--如果有更好的建议,请务必通知我。


1、介绍

SpEL支持在运行时 查询操作对象图。

2、功能概览

英文 中文
Literal expressions 字面值表达式
Boolean and relational operators 布尔和关系操作符
Regular expressions 正则表达式
Class expressions 类表达式
Accessing properties, arrays, lists, maps 访问properties、arrays、lists、maps
Method invocation 方法调用
Relational operators 关系操作符
Assignment 赋值
Calling constructors 调用构造器
Bean references bean引用
Array construction 构建数组
Inline lists 内联lists
Inline maps 内联maps
Ternary operator 三元操作符
Variables 变量
User defined functions 用户定义的功能
Collection projection 集合投影
Collection selection 集合选择
Templated expressions 模板化表达式

3、使用SpEL的API来evaluate 表达式

前提,搭建一个小环境,将Spring的基本jar包放进去。 -- 最简单的办法,使用STS新建一个Spring Boot项目即可。

然后,使用JUnit测试下面的代码:

package cn.larry.spel;

import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class TheTest {
@Test
public void run() {
ExpressionParser parser=new SpelExpressionParser();
String str="'Hello World'"; Expression exp = parser.parseExpression(str);
// Object value = exp.getValue();
String value = exp.getValue(String.class); System.out.println(value);
}
}

注意:仅限于不需要Spring容器的测试,就是仅限于直接调用API的测试。

你最有可能用到的SpEL相关的类和接口位于 org.springframework.expression 包及其子包,以及 spel.support 包中。

ExpressionParser接口,负责解析(parse)表达式字符串。

Expression接口,负责evaluate前面定义的表达式字符串 -- 是指ExpressionParser解析出来的吗?

再来看看SpEL如何调用方法

// demo for calling method
@Test
public void run1() {
ExpressionParser parser = new SpelExpressionParser();
String str = "'Hello World'.concat('!!!')"; Expression exp = parser.parseExpression(str);
Object value = exp.getValue(); System.out.println(value);
}

还有如何获取property -- 前提是JavaBean:

// demo for accessing property of JavaBean
@Test
public void run2() {
ExpressionParser parser = new SpelExpressionParser();
String str = "'Hello World'.bytes"; Expression exp = parser.parseExpression(str);
byte[] value = exp.getValue(byte[].class); System.out.println(value);
}

还支持获取nested property -- 前提是JavaBean:

// demo for accessing nested property of JavaBean
@Test
public void run3() {
ExpressionParser parser = new SpelExpressionParser();
String str = "'Hello World'.bytes.length"; Expression exp = parser.parseExpression(str);
int value = exp.getValue(int.class); System.out.println(value);
}

调用构造器

//demo for calling constructor
@Test
public void run4() {
ExpressionParser parser = new SpelExpressionParser();
String str = "new String('Hello World').toUpperCase()"; Expression exp = parser.parseExpression(str);
Object value = exp.getValue(); System.out.println(value);
}

上面的用法是直接解析一个expression string,但更常见的用法是根据表达式从特定对象中获取内容

"The more common usage of SpEL is to provide an expression string that is evaluated against a specific object instance (called the root object)."

有两种用法,具体选择哪种取决于你每次evaluate the expression时对象是否会发生改变。来看看两种代码:

/**
* The StandardEvaluationContext is relatively expensive to construct and during repeated usage
* it builds up cached state that enables subsequent expression evaluations to be performed more quickly.
* For this reason it is better to cache and reuse them where possible, rather than construct a new one for each expression evaluation.
*/
@Test
public void run5() {
GregorianCalendar cal = new GregorianCalendar();
cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("name"); // 利用目标对象(root object)构造一个context。开销相对较高,建议仅用于不常改变的对象。
EvaluationContext evaluationContext = new StandardEvaluationContext(inventor); inventor.setName("Newton"); // 还是会起作用 // 从指定的context中获取本表达式对应的值。
String value = (String) expression.getValue(evaluationContext);
System.out.println(value);
} // 如果root object可能发生改变,不建议使用其创建一个context,因为开销较高。应该如下:
@Test
public void run6() {
GregorianCalendar cal = new GregorianCalendar();
cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla--", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("name"); // 直接从目标对象中获取本表达式对应的值。内部还是会创建一个context。
// the expression evaluation infrastructure creates and manages a default evaluation context internally
String value = (String) expression.getValue(inventor);
System.out.println(value); inventor.setName("Newton"); // 也起作用了 value = (String) expression.getValue(inventor);
System.out.println(value);
}

这里的关键在于:Expression是从指定的上下文中获取相应的内容。粗略的说,Expression指定了要获取的property name,但还需要指定从什么地方获取该property value -- 是从EvaluationContext中获取的。

上面两个案例,run5() 是用目标对象(inventor)直接构造了一个StandardEvaluationContext,以供Expression从中获取值; run6() 则是让Expression直接从目标对象(inventor)中获取值 --但实际上内部还是构造了一个EvaluationContext。

二者的不同之处在于StandardEvaluationContext开销较大。

The StandardEvaluationContext is relatively expensive to construct and during repeated usage, it builds up cached state that enables subsequent expression evaluations to be performed more quickly. For this reason it is better to cache and reuse them where possible, rather than construct a new one for each expression evaluation.

In standalone usage of SpEL there is a need to create the parser, parse expressions and perhaps provide evaluation contexts and a root context object. However, more common usage is to provide only the SpEL expression string as part of a configuration file, for example for Spring bean or Spring Web Flow definitions. In this case, the parser, evaluation context, root object and any predefined variables are all set up implicitly, requiring the user to specify nothing other than the expressions.

3.1、EvaluationContext 接口

当evaluate an expression以获取properties、methods、fields以及帮助执行类型转换时,使用该接口。现成的实现类是 StandardEvaluationContext,利用了反射来操作对象、缓存 java.lang.reflect.Method、java.lang.reflect.Field和java.lang.reflect.Constructor的实例以增进性能。

你可以:

指定目标对象(root object):使用无参构造,再调用setRootObject();或者,使用有参构造。

也可以指定Expression中使用的变量和方法:setVariable()、registerFunction()。

还可以register custom ConstructorResolvers, MethodResolvers, and PropertyAccessors to extend how SpEL evaluates expressions。

详见JavaDoc。

类型转换

默认情况下,SpEL使用Spring core中的conversion service。支持泛型。

示例代码:

// SpEL默认使用Spring core的conversion service。默认支持泛型。
@Test
public void run8() {
class Simple {
public List<Boolean> booleanList = new ArrayList<Boolean>();
} Simple simple = new Simple();
simple.booleanList.add(true); StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); SpelExpressionParser parser = new SpelExpressionParser();
// false is passed in here as a string. SpEL and the conversion service will
// correctly recognize that it needs to be a Boolean and convert it
parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");// 不止能获取,还能设置。 // b will be false
Boolean b = simple.booleanList.get(0); System.out.println(b);
}

3.2、Parser configuration

通过设置Parser,可以改变某些默认的行为。典型的,例如 通过索引访问数组或集合,如果返回的是null,可以让它自动创建元素(empty element)。

例如,当你越界访问数组或list的元素时,可以让数组或list的长度自动增长!!!

代码如下:

// demo for parser configuration
@Test
public void run9() {
class Demo {
public List<String> list;
} // Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true, true); // 关键,见JavaDoc ExpressionParser parser = new SpelExpressionParser(config); Expression expression = parser.parseExpression("list[3]"); Demo demo = new Demo(); Object o = expression.getValue(demo); // 执行了才会修改原数据 // demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
System.out.println(demo.list);
}

3.3、SpEL compilation (略)

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#expressions-spel-compilation

请注意,上面的测试代码都是直接调用的API,所以不必启动环境(没有容器)。但下面的不同,多数需要启动环境(或者模拟环境RunWith)。


4、支持定义bean definition的表达式

SpEL可以用在基于XML或基于注解的配置元数据以定义BeanDefinition。语法: #{ <expression string> }

4.1、基于XML的配置

使用expression设置property或者constructor-args:

<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties -->
</bean>

上面的 T 表示类型,后面会讲到。

另外,系统已经预定义了 systemProperties,所以可以直接引用(官方文档用的是user.region,实际上是不存在的):

<bean id="person" class="cn.larry.demo.Person">
<property name="name" value="#{ systemProperties['user.name'] }"/> <!-- other properties -->
</bean>

还可以引用其他bean:

<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties -->
</bean> <bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/> <!-- other properties -->
</bean>

4.2、基于注解的配置

@Value 注解可以放在fields、methods、method/constructor的参数上,以注入默认值。

示例代码:

package cn.larry.demo;

import java.util.Set;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; /**
* 这里才是创建了一个容器。
*
* @author Larry
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class LarrySpringDemoSpelApplicationTests { @Test
public void contextLoads() {
} @Value("#{ systemProperties['user.country'] }")
// @Value("#{ systemProperties['java.vm.version'] }")
private String region; @Test
public void run1() {
System.out.println(region);
Set<Object> keySet = System.getProperties().keySet();
System.out.println(keySet);
}
}

5、Language Reference

5.1、字面值表达式 (literal expression)

字面值表达式支持的类型包括strings、dates、numeric values(int、real、hex)、boolean 以及 null。

字符串使用单引号间隔。如要输出单引号,两个单引号即可。(转义)

简单的示例如下(不需要容器),实际工作环境中往往是复合的复杂用法。

@Test
public void run11() {
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(); System.out.println(helloWorld);
System.out.println(avogadrosNumber);
System.out.println(maxValue);
System.out.println(trueValue);
System.out.println(nullValue);
}

数字支持:负数、指数、十进制小数。但默认real number是通过Double.parseDouble()解析的。

5.2、Properties、Arrays、Lists、Maps、Indexers

导航property ref很简单,例如:

// demo for property ref
@Test
public void run12() {
GregorianCalendar cal = new GregorianCalendar();
cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser(); // 利用目标对象(root object)构造一个context。开销相对较高,建议仅用于不常改变的对象。
EvaluationContext context = new StandardEvaluationContext(inventor); int year = (Integer) parser.parseExpression("Birthday.Year + 1900").getValue(context);
System.out.println(year);
}

注意:property name的首字母大小写可以忽略。数组和list以及map的内容都是通过[]获取的。不再演示。

5.3、inline lists 内联lists

lists可以通过 {} 直接填写内容,如无内容,代表empty list。如下:

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

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

嗯嗯嗯?For performance reasons, if the list is itself entirely composed of fixed literals then a constant list is created to represent the expression, rather than building a new list on each evaluation. -- 这意思得是说这种情况下每次evaluate用的其实都是同一个List?

5.4、inline maps 内联maps

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

类似inline lists,可以直接写在 {} 中,且 {:} 代表empty map。

inline maps特别之处在于,key可以不用单引号!--也可以用。

For performance reasons, if the map is itself composed of fixed literals or other nested constant structures (lists or maps) then a constant map is created to represent the expression, rather than building a new map on each evaluation. -- 类似inline maps,不再解释。

5.5、Array construction  构建数组

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

5.6、Methods

与正常的编码式调用方法一样,且也支持变量参数!

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

5.7、Operators 操作符

关系操作符 Relational operators

==、!=、<、<=、>、>=

// 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(Integer)").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);

务必小心primitive类型,在SpEL中它们会立即变成包装类型!所以, 1 instanceOf T(int) evaluates to false!而 1 instanceOf T(Integer) evaluates to true!!!

同jstl一样,==、!=、<、<=、>、>=也可以用eq、ne、lt、le、gt、ge表示。此外还可以:div (/), mod (%), not (!)。

逻辑操作符 Logical operators

and、or、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);

数学操作符 Mathematical operators

+操作符可以用于numbers和strings。-*/只能用于numbers。

另外还支持%和^。优先级同Java语法。

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

5.8、Assignment 赋值

可以通过expression的setValue进行赋值,也可以在getValue内部实现!代码如下:

// demo for assignment
@Test
public void run13() {
GregorianCalendar cal = new GregorianCalendar();
cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser(); // 利用目标对象(root object)构造一个context。开销相对较高,建议仅用于不常改变的对象。
EvaluationContext context = new StandardEvaluationContext(inventor); parser.parseExpression("name").setValue(context, "Alexander"); // way 1
parser.parseExpression("nationality = 'eng'").getValue(context); // way 2 System.out.println(inventor);
}

5.9、Types 类型

特殊的T操作符用于指定 java.lang下的类型。也可以通过该操作符调用静态方法。

使用该操作符引用java.lang下的类型时,可以省略包路径。但其他引用不能省略!

@Test
public void run14() {
SpelExpressionParser parser = new SpelExpressionParser();
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); System.out.println(dateClass);
System.out.println(stringClass);
System.out.println(trueValue); Class inventorClass = parser.parseExpression("T(cn.larry.demo.domain.Inventor)").getValue(Class.class);
System.out.println(inventorClass);
}

5.10、Constructors 构造器

使用 new操作符 可以调用构造器。

此时,除了primitive 类型和String类型之外,其他所有类型都必须是全路径

// demo for constructor. note this, besides primitive types and String, all types should be full qualified name.
@Test
public void run15() {
SpelExpressionParser parser = new SpelExpressionParser(); Inventor inventor = parser.parseExpression("new cn.larry.demo.domain.Inventor('Larry',new java.util.Date(),'cn')").getValue(Inventor.class);
System.out.println(inventor);
}

5.11、Variables 变量

可以引用变量,格式:#variableName。变量是使用StandardEvaluationContext的setVariable来设置的。如下:

// demo for variables. use setVariable method of StandardEvaluationContext to set variables.
@Test
public void run16() {
Inventor tesla = new Inventor("Nikola Tesla", new Date(), "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
context.setVariable("newName", "Mike Tesla"); // 这里,设置变量! SpelExpressionParser parser = new SpelExpressionParser();
parser.parseExpression("Name = #newName").getValue(context);// 这里,通过 #newName 引用! System.out.println(tesla.getName()); // "Mike Tesla"
}

#this、#root

The variable #this is always defined and refers to the current evaluation object (against which unqualified references are resolved). The variable #root is always defined and refers to the root context object. Although #this may vary as components of an expression are evaluated, #root always refers to the root.

// demo for #this and #root
@Test
public void run17() {
// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2, 3, 5, 7, 11, 13, 17)); StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("primes", primes); //set var ExpressionParser parser = new SpelExpressionParser();
// all prime numbers > 10 from the list (using selection ?{...}) evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]") // ? what is that for
.getValue(context);
System.out.println(primesGreaterThanTen);
}

奇怪,这里的 #primes.?[#this>10],其中.?是什么意思? #this难道是当前的变量??? -- 待研究。

5.12、Functions 功能(略)

通过注册自己的功能来扩展SpEL!

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#expressions-ref-functions

5.13、Bean references

如果evaluation context配置了一个bean resolver,就可以使用@符号来查找beans!

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 resolver的resolve(context, exp)来实现的!!!

当需要访问的是一个factory 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);

5.14、Ternary operator 三目运算符

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

5.15、Elvis operator

是三目运算符的简写,用于groovy语言中。

通常,如果使用三目运算符,会重复一个变量两次,例如:

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

Elvis操作符则简化了这一形式:

ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown'
@Value("#{systemProperties['pop3.port'] ?: 25}")

5.16、Safe Navigation operator  安全导航操作符

用于避免空指针异常,来自Groovy语言。

一般来说,当你引用一个对象时,在访问其property或method之前需要确认非null。而安全导航操作符则简单的返回一个null,而非抛出异常。

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

上面,这个?就是安全导航操作符? --用在对象后面!!!

5.17、Collection Selection 集合选择

选择是一个强大的表达式语言特色。

格式: ?[selectionExpression]。会过滤一个集合,返回一个新集合。如下:

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

上面的代码,会返回Members集合中Nationality是Serbian的Inventor。但是,搞不明白那个点的意思,Members是集合,集合还能用点?还是说???

即可用于list也可以用于map。对list来说,是对每个元素进行选择;对map来说,则是对每个键值对Map.Entry进行选择。

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

除了返回选择的元素之外,还可以直接返回第一个或最后一个:^[…]$[…]

5.18、Collection Projection 集合投影

格式:![projectionExpression]。

功能不好描述,但代码简单易懂。假定我们有一个Inventor的List,现在要用Inventor的name List,代码如下:

// demo for collection projection
@Test
public void run18() {
Inventor i1 = new Inventor("ant", new Date(), "cn");
Inventor i2 = new Inventor("egg", new Date(), "en");
Inventor i3 = new Inventor("grass", new Date(), "us"); List<Inventor> list = new ArrayList<Inventor>();
list.add(i1);
list.add(i2);
list.add(i3); SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("list", list); @SuppressWarnings("unchecked")
List<Inventor> value = (List<Inventor>) parser.parseExpression("#list.![name]").getValue(context);
System.out.println(value);
}

也可以map,略。

5.19、Expression templating 表达式模板化

表达式模板允许将字符串字面值与一个或多个evaluation blocks混合起来。每个evaluation block的前后缀都可以自定义,但默认使用 #{}。 如下:

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

关键是parseExpression()的第二个参数,是ParserContext类型。ParserContext接口用来影响表达式如何被解析以便支持模板功能。

TemplateParserContext接口的源码如下:

public class TemplateParserContext implements ParserContext {

    public String getExpressionPrefix() {
return "#{";
} public String getExpressionSuffix() {
return "}";
} public boolean isTemplate() {
return true;
}
}

6、用到的类

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#expressions-example-classes

上面这个地址是官方所用的类,与我的类肯定有出入--因为开始的时候没注意到官方还提供了类,所以逆推着写了自己的类,好在问题不大,就不再描述了。

我的代码可以来码云下载: SpringSpEL_Demo

ps:顺便输出了下SystemProperties的keySet:

[java.runtime.name, sun.boot.library.path, java.vm.version, java.vm.vendor, java.vendor.url, path.separator, java.vm.name, file.encoding.pkg, user.country, user.script, sun.java.launcher, sun.os.patch.level, PID, java.vm.specification.name, user.dir, java.runtime.version, java.awt.graphicsenv, org.jboss.logging.provider, java.endorsed.dirs, os.arch, java.io.tmpdir, line.separator, java.vm.specification.vendor, user.variant, os.name, sun.jnu.encoding, spring.beaninfo.ignore, java.library.path, java.specification.name, java.class.version, sun.management.compiler, os.version, user.home, user.timezone, java.awt.printerjob, file.encoding, java.specification.version, java.class.path, user.name, java.vm.specification.version, sun.java.command, java.home, sun.arch.data.model, user.language, java.specification.vendor, awt.toolkit, java.vm.info, java.version, java.ext.dirs, sun.boot.class.path, java.awt.headless, java.vendor, file.separator, java.vendor.url.bug, sun.io.unicode.encoding, sun.cpu.endian, sun.desktop, sun.cpu.isalist]

如果不知道Spring Boot,请先看这个:Spring Boot学习

如果不知道STS,请先看这个:Spring Tools Suite (STS) 简介。--需要先了解Spring Boot。

嗯,篇幅都很短,半个小时基本可以搞定,很简单的东西。

Spring 4 官方文档学习(五)核心技术之SpEL的更多相关文章

  1. Spring Boot 官方文档学习(一)入门及使用

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  2. Spring boot官方文档学习(一)

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  3. Spring 4 官方文档学习(十二)View技术

    关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...

  4. Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC

    内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...

  5. Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图

    接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...

  6. Spring 4 官方文档学习(十一)Web MVC 框架

    介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...

  7. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

    题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...

  8. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)

    接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...

  9. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion

    本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...

随机推荐

  1. tomcat支持https的历程

    tomcat真是业界良心啊,文档写的详细无比. 一.https是什么? 简单的说,就是http+SSL/TLS 协议还是http,但是在传输层过程中使用了加密(涉及握手.秘钥分发.加密.解密等过程). ...

  2. (二)RocketMq入门之消息发送和接收

    一.消息产生.发送 public class Producer { public static void main(String[] args) throws MQClientException { ...

  3. js实现多物体运动框架并兼容各浏览器

    首先,我们须要知道在js中获取对象的宽度如offsetWidth等.可能会存在一些小小的bug.原因之中的一个在于offsetWidth只不过获取盒子模型中内容的部分宽度.并不包括内边距,边框和外边距 ...

  4. 简易C#动态加载dll(实现插件化)

    可以通过该方法来实现程序插件化. 假设A,B两个类,A类为宿主,B类为插件需要加载到A类中: class Program { public interface IHellow { void Hello ...

  5. CentOS7添加logstash5启动脚本

    默认情况使用rpm包安装完logstash之后没有启动脚本,这一点我觉得算是开发不够彻底.官网给了一个脚本,需要根据不同的系统版本生成对应的启动脚本,而且官网没有给明使用方法,对于新用户来说算是个坑, ...

  6. python学习之itsdangerous模块

    类 from itsdangerous import URLSafeTimedSerializer as ustsr class ustsr(secret_key) 参数: secret_key可以是 ...

  7. Spark学习笔记总结-超级经典总结

    Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用.减少了延时处理,提高 ...

  8. SharePoint自动化系列——通过PowerShell创建SharePoint List Items

    转载请注明出自天外归云的博客园:http://www.cnblogs.com/LanTianYou/ 代码如下(保存到本地ps1文件中,右键run with PowerShell即可): Add-PS ...

  9. Permission denied (publickey). fatal: The remote end hung up unexpectedly 解决办法

    这两天学习git的时候,在本地创建了一个库,同时自己在GitHub上面也创建了一个库,照着廖老师的教程一步一步走到了push的环节突然出现了这样的错误: [zhangxiyu@localhost le ...

  10. Jar包版本查看方法

    原文:  https://blog.csdn.net/u011287511/article/details/66973559 打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目 ...