Spring 4 官方文档学习(五)核心技术之SpEL
题外话
官方文档用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 ConstructorResolver
s, MethodResolver
s, and PropertyAccessor
s 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 (略)
请注意,上面的测试代码都是直接调用的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!
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、用到的类
上面这个地址是官方所用的类,与我的类肯定有出入--因为开始的时候没注意到官方还提供了类,所以逆推着写了自己的类,好在问题不大,就不再描述了。
我的代码可以来码云下载: 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的更多相关文章
- Spring Boot 官方文档学习(一)入门及使用
个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...
- Spring boot官方文档学习(一)
个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...
- Spring 4 官方文档学习(十二)View技术
关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...
- Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC
内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...
- Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图
接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...
- Spring 4 官方文档学习(十一)Web MVC 框架
介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)
题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)
接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion
本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...
随机推荐
- iOS 关于 设计模式 与网友讨论实录
关于 设计模式 与网友讨论实录 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留 ...
- git配置gitignore
一.背景 ...
- C# 动态获取代码所在行号
通过System.Diagnostics.StackTrace获取代码所在行号和文件信息 获取行号信息 /// <summary> /// Get line number of code ...
- [转] 基本RS触发器
在触发器中,最简单的触发器是基本RS触发器,它由两个与-非门(或者两个或-非门)来组成. 图5.2.1(a)是由与-非门构成的基本RS触发器,由图看出,基本RS触发器有两个输入端(和)和两个输出端(和 ...
- javascript ES6模块化
一.将模块导出去 /* *将模块导出去 *a.js文件 */ const a=12; export default a; /* *将模块导出去 *b.js文件 */ const a=5; export ...
- Android Gradle 引用本地 AAR 的几种方式
折衷方案: 1.方式2 - 不完美解决办法2 2.再使用"自定义Gradle代码"来减轻重复设置的问题. 自定义Gradle代码如下: repositories { flatDir ...
- 用platformio编写arduino程序
哈哈,今天浏览arduino.cc官网,找到一个好东东,以后,在ubuntu下用终端编程在也不发愁了. platformio.org是一个物联网全能的IDE,甚至还贴心的提供了在命令行下编程的版本:p ...
- 最简短的openvpn的设置方式
这种方式对于测试能否连接到远程系统,十分的有用.尤其是国内复杂的网络环境下,检测一下,到底是服务器的原因,还是网络因素造成的,这是一个快捷的方式. 需要注意的是:这种方法是用明文连接.所有的加密措施都 ...
- 批处理学习笔记6 - 重定向符>和>>
重定向符大概有6,7种,和%符号差不多各有各的用途.这里学习>和>> > 是左边的值把右边的值覆盖 >> 是左边的值添加在右边的值上面 rem 是批处理的注释,类 ...
- 本地CS的导出xls代码段
用到之前发的NPOI的那个工具类库 //导出private void btnExport_Click(object sender, EventArgs e){saveFileDialog1.ShowD ...