Spring 官方文档翻译(第十章)

来源:互联网 发布:数控钻床钻孔编程范例 编辑:程序博客网 时间:2024/04/29 13:20

10 Spring Expression Language (SpEL)

10.1 简介

Spring Expression Language是一个强大的表达式语言,支持在运行时查询和操作对象。这个语言的语法跟Unified EL类似,但是他提供额外的功能,特别是方法调用和基本的字符串模板功能。

虽然Java也有几种Java表达式语言,像OGNL,MVEL和JBoss EL,而Spring的表达式语言为SPring提供了一个很好的支持表达式语言,可以用在Spring下的所有产品中。其语言特性是受Spring产品中的项目要求所驱动的,包括基于Spring Tool Suite的eclipse中代码完成支持的工具要求。也就是说,SpEL是基于不可知论API的技术,在需要的时候允许整合其他的表达式语言实现。

虽然Spring是作为Spring组合中表达式评估的基础,但是与Spring并没有直接的联系,所以他是可以低浓度使用的。本章的很多例子都是将SpEL当做一个独立的表达式语言看待,所以它需要一些引导的基础类,比如解析器。大多数Spring用户不需要了解这些基础设施,主需要评估一下表达式字符串。这个典型用途是整合SpEL到创建基于bean定义的XML或注解中(在 Expression support for defining bean definitions中有体现)。

本章涵盖表达式语言的特征,API以及语言语法。在一些地方,Inventor和Inventor的Society类用于表达式评估的目标对象。这些类的声明将在本章末尾列出。

10.2 功能概述

表达式语言支持以下功能:

  • 文字表达
  • 布尔和关系运算符
  • 正则表达式
  • 类表达式
  • 访问属性,数组,列表,map
  • 方法调用
  • 关系运算符
  • 分配(Assignment)
  • 调用构造函数
  • Bean引用
  • 数组构造
  • 内联列表
  • 内联map
  • Ternary operator
  • 变量
  • 用户自定义功能
  • Collection projection
  • Collection selection
  • 模板表达式

10.3 Spring的表达式接口评估表达式

本小节介绍SpEL接口和表达式语言的简单实用,完整的语言引用可以参考”“语言引用(Language Reference)”部分。

下面这些代码介绍使用SpELl API评估文字表达式”“Hello World”:


ExpressionParser parser = new SpelExpressionParser();Expression exp = paser.parseExpression("'Hello World'");String message = (String) exp.getValue();

message变量最后的值为‘Hello World’。

经常使用的SpEL类和接口位于包org.springframework.expression中和它的子包spel.support

接口ExpressionParser用于解析表达式字符串的,在这个例子中,表达式字符串是被单括号括起来的文字表达式。而接口Expression则是用来评估之前定义的表达式字符串。当调用parser.parseExpressionexp.getValue这两个方法时,需要分别抛出ParseException和EvaluationException异常。

SpEL支持很多功能,比如调用方法,访问属性和调用构造器。

一个方法调用的例子,我们调用文字字符串的concat方法:


ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression("'Hello World'.concat('!')");String message = (String) exp.getValue();

message的值是“Hello World!”。

一个调用JavaBean属性的例子,调用String的属性Bytes:


ExpressionParser parser = new SpelExpressionParser();// invokes 'getBytes()'Expression exp = parser.parseExpression("'Hello World'.bytes");byte[] bytes = (byte[]) exp.getValue();

SpEL也支持嵌套属性,用标准的.符号,像prop1.prop2.prop3和属性值的设置,也可以访问公共字段。


ExpressionParser parser = new SpelExpressionParser();// invokes 'getBytes().length'Expression exp = parser.parseExpression("'Hello World'.bytes.length");int length = (Integer) exp.getValue();

还可以调用String的构造器:


ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");String message = exp.getValue(String.class);

注意使用泛型方法public <T> T getValue(Class<T> desiredResultType)。使用这个方法不需要将表达式的值转成结果类型。如果值不能转成类型T或注册类型转换器转换不成功,会抛出EvaluationException异常。

SpEL更常用的方法是提供一个针对特定对象实例(根对象)进行评估的表达式字符串。有两个选项,选择哪一个取决于。在下面的例子中我们从Inventor类实例中获取name属性。


// Create and set a calendarGregorianCalendar c = new GregorianCalendar();c.set(1856, 7, 9);// The constructor arguments are name, birthday, and nationality.Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression("name");EvaluationContext context = new StandardEvaluationContext(tesla);String name = (String) exp.getValue(context);

例子中的最后一行的name变量的值为“Nikola Tesla”。StandardEvaluationContext类就是name属性被评估的地方,如果根对象不太可能会改变,在评估文本中只会被设置一次。如果根对象频繁的更改,就可以调用getValue,像下面例子一样:


// Create and set a calendarGregorianCalendar c = new GregorianCalendar();c.set(1856, 7, 9);// The constructor arguments are name, birthday, and nationality.Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression("name");String name = (String) exp.getValue(tesla);

在这个例子中tesla直接用到getValue中,表达式评估设备创建和管理一个默认的评估上下文-它不需要提供。

相对来说,创建StandardEvaluationContext的成本略高,因为在重复利用中建立缓存状态,能够更快的执行下后续的表达式评估。基于这个原因,要尽可能的缓存和重利用,而不是为每一个表达式创建一个新的评估。

在某些情况下可能需要使用配置的评估上下文,但在调用getValue时仍然需要提供一个不同的根对象。getValue在同一个调用中,可以允许两个特例。在某些情况下,调用的根对象被认为是覆盖了评估上下文的任何(可能为空)特例。

作为最后一个例子,用上面的例子讲一个显示布尔类型的用法:


Expression exp = parser.parseExpression("name == 'Nikola Tesla'");boolean result = exp.getValue(context, Boolean.class); // evaluates to true

10.3.1 EvaluationContext接口

EvaluationContext接口用在评估需要解决属性,方法,字段的表达式,来执行类型转换的需求。StandardEvaluationContext使用反射来操作对象,缓存java.lang.reflect.Methodjava.lang.reflect.Fieldjava.lang.reflect.Constructor实例以提高性能。

StandardEvaluationContext是根据方法setRootObject()或把根对象放入构造器中来评估根对象。同样也可以用方法setVariable()registerFunction()用在表达式中特殊的变量和功能。StandardEvaluationContext也是可以注册自定义ConstructorResolverMethodResolverPropertyAccessor来扩展SpEL评估表达式的地方。

类型转换

SpEL默认使用Spring core(org.springframework.core.convert.ConversionService)的转换服务。这个转换服务有很多内置的常用的转换器,但也可以扩展添加类型之间自定义转换器。此外,它具有泛型感知的强大功能,这意味着,当在表达式中含有泛型时,SpEL将会尝试转换以维持任何对象类型正确性。

在实际开发中怎么用呢?假设使用setValue()被设置一个List属性,这个属性实际上是List。SpEL将会在被放置之前把list元素转成Boolean。一个简单例子:


class Simple {public List<Boolean> booleanList = new ArrayList<Boolean>();}Simple simple = new Simple();simple.booleanList.add(true);StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple);// 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 itparser.parseExpression("booleanList[0]").setValue(simpleContext, "false");// b will be falseBoolean b = simple.booleanList.get(0);

10.3.2 解析配置

用解析配置对象(org.springframework.expression.spel.SpelParserConfiguration)可以配置SpEL表达式解析,配置对象控制表达式组件的行为。例如,如果数组或集合的某个索引元素为null,配置对象就可以自动创建一个元素。这对使用一组属性引用的表达式很有用处。当索引数值超过数组或列表本身长度时,会自动的增加他们的长度。


class Demo {    public List<String> list;}// Turn on:// - auto null reference initialization// - auto collection growingSpelParserConfiguration config = new SpelParserConfiguration(true,true);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

当然还可以配置SpEL编译器的行为。

10.3.3 SPEL编译

Spring Framework 4.1包含一个基本的表达式编译器。表达式通常在评估阶段提供很多动态灵活功能,而不是最优的性能。对于偶然的几次使用表达式这是没问题的,但是如果是其他组件(比如Spring Integration)使用的话,需要的是性能而不是动态灵活。

而新的SpEL编译器打算处理这个需求。编译器将在评估过程中生成一个Java类以体现表达式的行为,这样可以获得更快的表达式评估。由于缺少表达式类,编译器在执行编译时会在评估阶段就行信息收集。例如,刚开始时,编译器不知道引用属性的类型,但是通过第一次的解释性评估,它就知道类型是什么。当然,如果不同的表达式元素随时间而变化的话,编译器获得的信息接下来也会有问题发生。所以编译器适用于类型信息不会发生改变的表达式。

一个基本的表达式像这样:


someArray[0].someProperty.someOtherProperty < 0.1

表达式设计数组访问,属性的引用和数值运算,能很明显的看到性能的提升。5000此迭代的例子中,之前的评估需要75ms,而使用编译器评估只需要3ms。

编译器配置

编译器不是默认开启的,有两种方式开启。一种是在之前讲的解析配置中打开,另一种是通过SpEL嵌入到其他组件的系统属性打开。本小节会讨论这两种操作。

编译器运行的模式都在枚举org.springframework.expression.spel.SpelCompilerMode中。模式如下:

  • OFF - 默认关闭。
  • IMMEDIATE - 立即模式,表达式会被尽快执行。这个模式通常是在第一次解释性评估之后。如果执行失败,会收到异常报告。
  • MIXED - 混合模式中,表达式会在解释模式和编译模式之间切换。经过一些解释运行后会转成编译模式;然后如果编译格式报错(像之前讲的类型变换),就自动转成解释模式,然后循环往复。基本上IMMEDIATE模式的异常都是内部自己解决了。

IMMEDIATE模式存在的目的是MIXED模式会导致有副作用的表达式问题。如果一个编译器表达式在部分成功后爆炸,可能会影响系统的状态。在这种情况下,调用应该不会重新运行这个模式,因为一些表达式需要运行两次。

选择运行模式之后,用SpelParserConfiguration配置解析器:


SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,this.getClass().getClassLoader());SpelExpressionParser parser = new SpelExpressionParser(config);Expression expr = parser.parseExpression("payload");MyMessage message = new MyMessage();Object payload = expr.getValue(message);

当编译器模式确定时,也可以指定类加载器(允许传null值),编译器表达式被定义在任何提供的子类加载器中。重要的是确保子类加载器是否确定,它能看到表达式评估过程中的所有类型。如果子类加载器没有确定的话,会直接使用默认的(通常是表达式评估期间运行的线程的上下文类加载器)。

第二种方法也即是SpEL嵌入到其他组件中时,不太可能通过配置对象进行配置。这种情况下就会使用系统属性。spring.expression.compiler.mode属性可以设置为SpelCompilerMode枚举值(off, immediate, 或mixed)的其中一个。

编译器限制

虽然已经有了Spring Framework 4.1的基本编译器框架,但是它并不支持表达式的每个类型。最初的关注点是在关键环境中常用的表达式。以下是不支持的表达式:

  • 涉及到分配的表达式;
  • 依赖转换器服务的表达式;
  • 使用自定义解析器或表达器的表达式;
  • 使用selection or projection的表达式。

10.4 Expression support for defining bean definitions

SpEL表达式可以与用于定义BeanDefinitionXML配置元素的XML或注解两种方法一起使用。在这两种情况下,定义表达式的格式为#{<expression string>}

10.4.1 基于XML配置

属性值或构造器参数可以用表达式表示:


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

systemProperties变量被重新定义,所以你可以在自己的表达式中使用。不需要在重新定义的变量前加#


<bean id="taxCalculator" class="org.spring.samples.TaxCalculator"><property name="defaultLocale" value="#{ systemProperties['user.region'] }"/><!-- other properties --></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 -->

10.4.2 注解配置

@Value注解可以放置在字段,方法或者方法/构造器参数上,表示默认的值。

下面这个例子表示设置一个字段变量的默认值:


public static class FieldValueTestBean    @Value("#{ systemProperties['user.region'] }")    private String defaultLocale;    public void setDefaultLocale(String defaultLocale) {        this.defaultLocale = defaultLocale;    }    public String getDefaultLocale() {        return this.defaultLocale;    }}

以下是展示在setter方法上注解:


public static class PropertyValueTestBean    private String defaultLocale;    @Value("#{ systemProperties['user.region'] }")    public void setDefaultLocale(String defaultLocale) {        this.defaultLocale = defaultLocale;    }    public String getDefaultLocale() {        return this.defaultLocale;    }}

注解用到参数中的例子:


public class SimpleMovieLister {    private MovieFinder movieFinder;    private String defaultLocale;    @Autowired    public void configure(MovieFinder movieFinder,            @Value("#{ systemProperties['user.region'] }") String defaultLocale) {        this.movieFinder = movieFinder;        this.defaultLocale = defaultLocale;    }    // ...}


public class MovieRecommender {    private String defaultLocale;    private CustomerPreferenceDao customerPreferenceDao;    @Autowired    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,            @Value("#{systemProperties['user.country']}") String defaultLocale) {        this.customerPreferenceDao = customerPreferenceDao;        this.defaultLocale = defaultLocale;    }    // ...}

10.5 Language Reference

10.5.1 文字表达式

文字表达式支持的类型有字符串,数值(int,real,hex),布尔和null。字符串类型用单引号分割。还要这个但因好的字符型放到双引号中。

下面例子是文字表达式的简单使用。通常,实际使用中他不会这么简单,而是作为更复杂的表达式,比如说在正则表达式的一侧。


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 2147483647int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();boolean trueValue = (Boolean) parser.parseExpression("true").getValue();Object nullValue = parser.parseExpression("null").getValue();

10.5.2 Properties, Arrays, Lists, Maps, Indexers

属性引用是很简单的:只需要用‘.’表达嵌套的属性值:


// evals to 1856int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);

数组和列表的例子:


ExpressionParser parser = new SpelExpressionParser();// Inventions ArrayStandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla);// evaluates to "Induction motor"String invention = parser.parseExpression("inventions[3]").getValue(    teslaContext, String.class);// Members ListStandardEvaluationContext 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的例子:


// Officer's DictionaryInventor 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 valuesparser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(        societyContext, "Croatia");

10.5.3 Inline lists

Lists可以用{}表达:


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

{}本身表达一个空的list。处于对性能考虑,如果列表都是由文字组成,系统会创建一个常量列表而不是每个评估都会建一个新的列表。

10.5.4 Inine Maps

Maps可以直接用{key:value}表示:


// evaluates to a Java map containing the two entriesMap 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。为性能考虑同上。

10.5.5 Array construction

可以用Java数组构建数组,在构造时就可以 初始化填充这个数组:

int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);// Array with initializerint[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);// Multi dimensional arrayint[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

当构造多维数组时目前不充许提供初始化。

10.5.6 Methods

方法包括经典的Java语法。

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

10.5.7 Operators

Relational operators

关系运算符包括“==”“!=”“<””<=” “>” “>=”.

// evaluates to trueboolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);// evaluates to falseboolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);// evaluates to trueboolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

除了支持关系运算,SpEL还支持instanceofmatches运算:

// evaluates to falseboolean falseValue = parser.parseExpression(        "'xyz' instanceof T(Integer)").getValue(Boolean.class);// evaluates to trueboolean trueValue = parser.parseExpression(        "'5.00' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);//evaluates to falseboolean falseValue = parser.parseExpression(        "'5.0067' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

每个字符操作可以单纯的用字母代替。因为这可以米面字符在文档中有特殊含义。以下是等价的符号: lt (<), gt (>), le (), ge (>=), eq (==), ne (!=), div (/), mod (%), not (!)。不区分大小写。

Logical operators

逻辑运算符有and,or和not。

// -- AND --// evaluates to falseboolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);// evaluates to trueString expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);// -- OR --// evaluates to trueboolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);// evaluates to trueString expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);// -- NOT --// evaluates to falseboolean 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

// Additionint two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2String testString = parser.parseExpression(        "'test' + ' ' + 'string'").getValue(String.class); // 'test string'// Subtractionint four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000// Multiplicationint six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0// Divisionint minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0// Modulusint three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1// Operator precedenceint minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21

10.5.8 Assignment

Inventor inventor = new Inventor();StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");// alternativelyString aleks = parser.parseExpression(        "Name = 'Alexandar Seovic'").getValue(inventorContext, String.class);

10.5.9 Types

T操作符代表java.lang.Class(the type)的一个实例。

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

10.5.10 Constructors

类名不能与基本类型和String一样。

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 Listp.parseExpression(        "Members.add(new org.spring.samples.spel.inventor.Inventor(            'Albert Einstein', 'German'))").getValue(societyContext);

10.5.11 Variables

表达式中的变量的表达式语法#variableName

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"

The #this and #root variables

#this 变量指的是当前的评估对象,#object 变量指的是根上下文对象。

// create an array of integersList<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 integersExpressionParser 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);

10.5.12 Functions

可以通过注册可以在表达式字符串中调用的用户定义的函数来扩展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);

10.5.13 Bean references

如果使用bean解析器配置了评估上下文,则可以通过(@)符号在表达式中查找beans:

ExpressionParser parser = new SpelExpressionParser();StandardEvaluationContext context = new StandardEvaluationContext();context.setBeanResolver(new MyBeanResolver());// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluationObject bean = parser.parseExpression("@foo").getValue(context);

要访问工厂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 evaluationObject bean = parser.parseExpression("&foo").getValue(context);

10.5.14 Ternary Operator (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"

10.5.15 Elvis Operator

Elvis操作符缩短了三元操作符,在Groovy语言中使用。在三元操作符中,需要重复一个变量两次,例如:

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

相反,你可以使用Elvis操作符,命名与Elvis相似:

ExpressionParser parser = new SpelExpressionParser();String name = parser.parseExpression("name?:'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 Teslatesla.setName(null);name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);System.out.println(name); // Elvis Presley

10.5.16 Safe Navigation operator

安全导航运算符用于避免抛出NullPointerException异常,它来自于Groovy语言。通常在引用一个对象时,需要在访问对象的方法或属性之前验证它是否为空。为了避免这种情况,安全导航操作会简单的返回一个null,而不是抛出异常。

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); // Smiljantesla.setPlaceOfBirth(null);city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);System.out.println(city); // null - does not throw NullPointerException!!!

10.5.17 Collection Selection

选择是表达式语言中的一个强大的功能,它允许你通过从一些条目里选择将一些源集合转化为另外一个。

选择用的语法是.?[selectionExpression]。它会过滤掉这个集合,然后返回一个包含原始元素子集的新集合。例如,选择将很快得到Serbian发明者的列表:

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

列表和map都可以进行选择。在前一种情况下,根据单独列表元素进行评估标准,同时针对map,根据每个map的entry进行评估标准(Java类型Map.Entry)。Map输入中有key和value,可以作为选择中访问的属性。

下面这个表达式将会返回一个由原始元素集合组成的新集合,其中value都小于27:

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

为了返回所有选定元素,还可以检索第一个或最后一个值。匹配第一个元素的语法^[...],匹配最后一个元素的语法是$[...]

10.5.18 Collection Projection

Projection(翻译不出来)允许一个集合驱动子表达式的评估,返回一个新的集合,它的语法是![projectionExpression]。更容易理解的例子,假设有一个inventors的列表,我们想找到他们出生城市的列表。我们将会评估每个inventor输入‘placeOfBirth.city’。使用projection:

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

当然,map也可以使用,同上。

10.5.19 Expression templating

表达式模板允许文本域一个或多个评估块混合一起。每个评估模块都可以用自定义的前缀和后缀进行分隔,常用#{}作为分隔符。例如:

String randomPhrase = parser.parseExpression(        "random number is #{T(java.lang.Math).random()}",        new TemplateParserContext()).getValue(String.class);// evaluates to "random number is 0.7038186818312008"
public class TemplateParserContext implements ParserContext {    public String getExpressionPrefix() {        return "#{";    }    public String getExpressionSuffix() {        return "}";    }    public boolean isTemplate() {        return true;    }}

10.6 例子中用到的类

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;    }}
0 0
原创粉丝点击