SpEL.docx

上传人:b****5 文档编号:6995084 上传时间:2023-01-15 格式:DOCX 页数:20 大小:38.51KB
下载 相关 举报
SpEL.docx_第1页
第1页 / 共20页
SpEL.docx_第2页
第2页 / 共20页
SpEL.docx_第3页
第3页 / 共20页
SpEL.docx_第4页
第4页 / 共20页
SpEL.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

SpEL.docx

《SpEL.docx》由会员分享,可在线阅读,更多相关《SpEL.docx(20页珍藏版)》请在冰豆网上搜索。

SpEL.docx

SpEL

Spring表达式语言参考手册

译:

刘海刚

SpEL使用

跟着下面的例子简单了解一下SpEL:

ExpressionParserparser=newSpelExpressionParser();

Expressionexp=parser.parseExpression("'HelloWorld'");

Stringmessage=(String)exp.getValue();

这个例子将简单的返回“HelloWorld”值给message变量。

你最有可能使用的Spel类和接口位于包org.springframework.expression及其子包和spel.support中。

ExpressionParser接口负责解析字符串表达式,在该示例中,表达式就是那个用单引号包围的字符串,Expression接口负责评估给定的字符串表达式,当调用parser.parseExpression和exp.getValue方法时,可能会抛出ParseException和EvaluationException异常。

SpEL支持各种功能,如调用方法,访问属性和调用构造函数。

下面我们在字符串表达式中使用concat方法来演示方法调用的例子:

ExpressionParserparser=newSpelExpressionParser();

Expressionexp=parser.parseExpression("'HelloWorld'.concat('!

')");

Stringmessage=(String)exp.getValue();

现在message的值变成了'HelloWorld!

'(后面多了一个“!

”)

为了演示JavaBean属性调用的例子,我们以获取字符串的Bytes为例

ExpressionParserparser=newSpelExpressionParser();

//实际调用的是'getBytes()'

Expressionexp=parser.parseExpression("'HelloWorld'.bytes");

byte[]bytes=(byte[])exp.getValue();

Spel还支持嵌套属性,使用标准点符号,即prop1.prop2.prop3和属性值的设置。

也可以直接调用公共字段。

ExpressionParserparser=newSpelExpressionParser();

//实际调用的是'getBytes().length'

Expressionexp=parser.parseExpression("'HelloWorld'.bytes.length");

intlength=(Integer)exp.getValue();

也可以直接使用Spring构造器,而不是字符串形式的Spring

ExpressionParserparser=newSpelExpressionParser();

Expressionexp=parser.parseExpression("newString('helloworld').toUpperCase()");

Stringmessage=exp.getValue(String.class);

需要注意的是使用通用方法publicTgetValue(ClassdesiredResultType)。

使用此方法不需要将表达式的值转换为所需的结果类型。

如果该值不能转换为类型T或使用注册的类型转换器转换,则将抛出EvaluationException。

更加通用的做法是提供一个专门类型对象(成为根对象)作为表达式字符串的评估对象。

至于具体使用那种方式,取决于字符串表达式每次调用是否不允许改变表达式的值,换句话说第一种方式每次都是new对象,而下面的方式是在同一个对象中。

在下面的示例中,我们从Inventor类的实例中得到name属性值。

//创建一个Calendar对象,并设置日期

GregorianCalendarc=newGregorianCalendar();

c.set(1856,7,9);

//Inventor是一个自定义JavaBean,构造器参数为name,birthday及nationality。

Inventortesla=newInventor("NikolaTesla",c.getTime(),"Serbian");

ExpressionParserparser=newSpelExpressionParser();

Expressionexp=parser.parseExpression("name");

EvaluationContextcontext=newStandardEvaluationContext(tesla);

Stringname=(String)exp.getValue(context);

在最后一行中,name的值将被设置为"NikolaTesla",StandardEvaluationContext类可以设置哪个对象的name属性将参与评估。

这就是第二种方法的使用场景,即根对象不太会改变,通过评估上下文对象(EvaluationContext)简单的把根对象设置一次。

如果根对象随时会发生改变,则可以在每次调用getValue时提供该对象,如下例所示:

//创建一个Calendar对象,并设置日期

GregorianCalendarc=newGregorianCalendar();

c.set(1856,7,9);

//Inventor是一个自定义JavaBean,构造器参数为name,birthday及nationality。

Inventortesla=newInventor("NikolaTesla",c.getTime(),"Serbian");

ExpressionParserparser=newSpelExpressionParser();

Expressionexp=parser.parseExpression("name");

Stringname=(String)exp.getValue(tesla);

在这种情况下,Inventor的tesla对象实例将直接提供给gatValue方法,表达式评估的基础架构将在内部构建和管理默认的评估上下文,而不需要显示的提供了。

StandardEvaluationContext构造的开销比较昂贵,但在重复使用时,它会建立缓存,以便能够更快地执行后续表达式评估。

因此,最好是在需要缓存和重复使用的时候来构建它,而不是每个表达式评估都建立一个新的。

在某种情况下,可能需要对评估上下文进行配置,同时在getValue方法中又指定了不同的根对象,getValue允许在同一次调用中两者都指定,此时传递给getValue的根对象将覆盖评估上下文中指定的任何对象(可能null)。

单独使用SpEL时,需要创建解析器,解析表达式,还可能创建评估上下文及根对象。

但更常见的做法是仅使用表达式字符串作为配置的一部分。

在这种情况下,解析器,评估上下文,根对象和任何预定义的变量都是隐式设置的,除用户有特殊要求外,表达式之外的对象均不要指定。

作为最后一个介绍性示例,使用上个示例中的Inventor对象来显示布尔运算符的使用。

Expressionexp=parser.parseExpression("name=='NikolaTesla'");

booleanresult=exp.getValue(context,Boolean.class);//评估为true

EvaluationContext接口

EvaluationContext接口主要用于解析属性、方法、字段及协助类型转换时,开箱即用,EvaluationContext通过类反射来操作对象,缓存java.lang.reflect.Method,java.lang.reflect.Field,和java.lang.reflect.Constructor实例来提高性能。

可以通过调用StandardEvaluationContext接口的setRootObject方法来设置根对象,或者通过构造器参数将根对象进行设置以待评估。

还可以使用setVariable()和registerFunction()方法指定将在表达式中使用的变量和函数。

StandardEvaluationContext还可以注册自定义的ConstructorResolvers,MethodResolvers和PropertyAccessors,以扩展SpEL如何评估表达式。

相关请参考这些类的JavaDoc了解更多详细信息。

类型转换

默认情况下,SpEL使用Spring核心(org.springframework.core.convert.ConversionService)中提供的转换服务。

此转换服务附带许多转换器,内置了常用的转换,但也可自行扩展,因此可以添加类型之间的自定义转换。

此外,它具有泛型感知的关键功能,这意味着在使用表达式中的泛型类型时,SpEL将尝试转换正确的对象类型。

这在实践中意味着什么呢?

假如利用setValue方法设置一个List属性,属性的实际类型是List,SpEL将知道列表元素需要转换为Boolean类型。

如下例所示:

classSimple{

publicListbooleanList=newArrayList();

}

Simplesimple=newSimple();

simple.booleanList.add(true);

StandardEvaluationContextsimpleContext=newStandardEvaluationContext(simple);

//此处用一个字符串形式的false是可以的,SpEL的转换服务会知道需要将它转换为Boolean类型

parser.parseExpression("booleanList[0]").setValue(simpleContext,"false");

//b将为false

Booleanb=simple.booleanList.get(0);

解析器配置

可以使用解析器配置对象(org.springframework.expression.spel.SpelParserConfiguration)来配置SpEL表达式解析器。

解析器配置对象可以控制一些表达式组件的行为,比如索引到数组或集合中的元素为null,则可以自动创建元素,当在使用一组由属性链表达式时是非常有用的,如果索引到的数组或集合超出了列表的大小,则可以自动的扩展数组或集合的长度以满足索引要求。

classDemo{

publicListlist;

}

//打开:

//-自动为null引用构造

//-自动增长集合

SpelParserConfigurationconfig=newSpelParserConfiguration(true,true);

ExpressionParserparser=newSpelExpressionParser(config);

Expressionexpression=parser.parseExpression("list[3]");

Demodemo=newDemo();

Objecto=expression.getValue(demo);

//demo.list现在有4个实体元素

//每个元素是一个空的String

SpEL编译

SpringFramework4.1包含一个基本的表达式编译器。

表达式通常被解释为在评估过程中提供了大量的动态灵活性,但不能提供最佳性能。

偶尔使用一次表达式也许还可以接受,但像被SpringIntegration组件一样使用时,性能就显得非常重要,而没有真正的实现动态。

新的SpEL编译器旨在满足这一需求。

编译器将在评估过程中生成一个真正的Java类,体现了表达式的行为,并使用它来实现更快的表达式求值。

由于在表达式中缺少类型的表达(表达式不是真正通过键盘输入的代码),所以编译器在执行编译时会在表达式的解释性评估期间收集的信息。

例如,它不仅仅是从表达式中知道属性引用的类型,而是在第一个解释性评估过程中会发现它是什么。

当然,如果各种表达式元素的类型随着时间的推移而变化,那么基于此信息的编译可能会导致麻烦。

因此,编译最适合于重复评估类型信息不会改变的表达式。

对于这样的表达式:

someArray[0].someProperty.someOtherProperty<0.1

这涉及到数组访问,某些属性和数字操作,性能增益可以非常明显。

在50000次迭代的微型基准运行示例中,解释器只需要75ms进行翻译,使用编译后的表达式只需要3ms。

编译器配置

默认情况下,编译器未打开,但有两种方法可以打开它。

可以使用先前讨论的解析器配置过程或者当将SpEL使用嵌入到另一个组件中时通过系统属性打开它。

本节讨论这些选项。

首先要明白,编译器可以运行几种模式,在枚举(org.springframework.expression.spel.SpelCompilerMode)中可以得到。

枚举值如下:

●OFF:

编译器关闭;这是默认值。

●IMMEDIATE:

在即时模式下,表达式将尽快编译。

这通常是在第一次解释性评估之后。

如果编译的表达式失败(通常是由于类型更改,如上所述),则在表达式求值的调用时会抛出异常。

●MIXED:

在混合模式下,表达式随着时间的推移在解释模式和编译模式之间静默地切换。

经过一些解释运行后,它们将切换到编译形式,如果编译后的表单出现问题(如上所述改变类型),表达式将自动重新切换回解释形式。

稍后,它可能生成另一个编译表单并切换到上面。

基本上,用户进入即时模式(IMMEDIATE)的异常是内部处理。

之所以要存在即时(IMMEDIATE)模式,是因为混合模式(MIXED)在表达式问题上可能会产生副作用,如果表达式在部分编译成功时受到冲击,此时可能已经发生了影响系统状态的操作,如果发生此类事件,调用者可能并不希望隐式的切换到解释模式重新运行,因为这样的话,部分表达式会被运行两次。

当编译器模式选择后,使用SpelParserConfiguration来配置解析器:

SpelParserConfigurationconfig=newSpelParserConfiguration(SpelCompilerMode.IMMEDIATE,this.getClass().getClassLoader());

SpelExpressionParserparser=newSpelExpressionParser(config);

Expressionexpr=parser.parseExpression("payload");

MyMessagemessage=newMyMessage();

Objectpayload=expr.getValue(message);

当指定编译器模式时,也可以指定一个类加载器(允许传递null)。

编译后的表达式将被定义在以任何提供的方式创建的子类加载器中。

指定一个类加载器是很重要的,因为这样在表达式评估处理的时候对所有涉及到的类型才可见,如果没有指定,则使用运行时线程的上下文类加载器,

当SpEL被嵌入到其他组件中时,此时无法通过编程方式来配置对象时,可以通过使用系统属性piler.mode的方式来配置编译器,属性值为SpelCompilerMode的枚举值之一(off、immediate或mixed)。

编译器限制

在spring框架4.1中有基本的编译框架。

然而,框架不支持编译每一种表达式。

重点会关注在通用的表达式可以在一些严酷性能的上下文中运行。

这是这些表达式不能被编译:

●用于分配的表达式

●依赖转换服务的表达式

●使用自定义解析器或访问器的表达式

●使用选择或映射的表达式

在未来越来越多类型的表达式将会被编译。

表达式在Bean定义中的支持

本次调研不涉及此部分的内容,因此未翻译。

表达式语言参考

文本表达式

支持文本表达式的类型有字符串、数值(整数,实数,十六进制)、布尔型和空(null)。

字符串使用单引号分隔,如果文本中含有单引号,则以两个单引号代替(转义)。

下面的示例显示了文本的简单用法,一般不会这样使用,而是作为用于相对复杂表达式的一部分,比如作为逻辑运算中的比较对象。

ExpressionParserparser=newSpelExpressionParser();

//evalsto"HelloWorld"

StringhelloWorld=(String)parser.parseExpression("'HelloWorld'").getValue();

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

//evalsto2147483647

intmaxValue=(Integer)parser.parseExpression("0x7FFFFFFF").getValue();

booleantrueValue=(Boolean)parser.parseExpression("true").getValue();

ObjectnullValue=parser.parseExpression("null").getValue();

数字支持使用负号,指数符号和小数点。

一般使用Double.parseDouble()解析实数。

Properties,Arrays,Lists,Maps,Indexers

使用属性引用很简单,只需要一个“.”来表示嵌套属性值。

例如Inventor的两个实例pupin和tesla,被填充好数据后,我们用这种表达式方式得到tesla的出生年份和pupin的出生城市:

//evalsto1856

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

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

属性名的第一个字母不区分大小写,数组和集合的元素内容使用方括号的方式进行获取。

ExpressionParserparser=newSpelExpressionParser();

//InventionsArray

StandardEvaluationContextteslaContext=newStandardEvaluationContext(tesla);

//evaluatesto"Inductionmotor"

Stringinvention=parser.parseExpression("inventions[3]").getValue(

teslaContext,String.class);

//MembersList

StandardEvaluationContextsocietyContext=newStandardEvaluationContext(ieee);

//evaluatesto"NikolaTesla"

Stringname=parser.parseExpression("Members[0].Name").getValue(

societyContext,String.class);

//ListandArraynavigation

//evaluatesto"Wirelesscommunication"

Stringinvention=parser.parseExpression("Members[0].Inventions[6]").getValue(

societyContext,String.class);

Map的内容是通过方括号中使用文本作为键值进行获取,此时,Map类型对象Officers的键值是字符串类型,因此我们可以这样做:

//官职字典表

Inventorpupin=parser.parseExpression("Officers['president']").getValue(

societyContext,Inventor.class);

//evaluatesto"Idvor"

Stringcity=parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(

societyContext,String.class);

//settingvalues

parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(

societyContext,"Croatia");

内联List

集合/数组可以直接在表达式中使用“{}”符号来表示。

//evaluatestoaJavalistcontainingthefournumbers

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

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

“{}”表示一个空的集合/数组,出于性能原因,如果列表本身完全由固定文字组成,则会创建一个常量列表来表示表达式,而不是在每次评估的时候都建立一个新的集合/数组

内联Map

可以使用{key:

value}表示法直接在表达式中表示Map。

//evaluatestoaJavamapcontainingthetwoentries

MapinventorInfo=(Map)parser.parseExpression("{name:

'Nikola',dob:

'10-July-1856'}").getValue(context);

MapmapOfMaps=(Map)parser.parseExpression("{name:

{first:

'N

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 院校资料

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1