五分钟学会Spring表达式语言SpEL,不但学会使用也知道底层原理?

  #头条群星9月榜#

  

  sprign 表达式

  SpEL:Spring Expression Language,支持在运行时查询和操作对象图的一种强大的表达式语言。该语言的语法类似于Unified EL,但提供了额外的特性,最显著的是方法调用和基本的字符串模板功能。SpEL为Spring社区提供表达式语言的支持,但是并没有和Spring绑定,可以单独使用。

  

  几个重要接口类

  接口ExpressionParser类,这个接口主要用来解析表达式字符串,并且返回一个表达式对象Expression,这个接口ExpressionParser有个安全并且可以重用的实现类SpelExpressionParser。

  通过一个例子了解解析字符串字面量(string literal),比如表达式'Hello World',程序执行完输出字符串Hello World,这个字符串需要使用单引号括起来,先看看测试程序,后续再了解字符串字面量的语法,代码如下:

  如果不使用单引号,程序则抛出SpelParseException异常,从异常信息看是解析完表达式后面还有数据导致,主要是字符串字面量中有空格导致,如果字符串改变成'Hello_World'则会正常解析,测试结果如下:

  再简单分析类Expression,这个类会根据上下文对自身求值,并封装了求值的公共方法。常用的方法是getValue(),这个方法有多个重载的版本,如果没有传参desiredResultType(预期的结果类型),怎需要对结果类型进行转换,如果类型不能转换则抛出SpelEvaluationException异常。

  最后了解一下接口EvaluationContext,这个接口用来解析属性、方法、字段,并帮助执行类型转换。在类定义了很多get方法,例如获取rootObject,MethodResolvers,BeanResolver等,并且有个setVariable方法,用于设置变量。这个接口的实现类有三个StandardEvaluationContext、SimpleEvaluationContext和MethodBasedEvaluationContext。

  

  表达式求值

  1、字面量表达式,支类型包括字符串、数值(int、real、十六进制)、布尔值和null。字符串需要使用单引号括起来,如果字符中有单引号需要使用两个单引号。下面是几个例子:

  2、获取对象属性,数组,列表,映射,索引:首先定义一个测试类,类中有数组、列表和映射(Map)等

  首先测试获取对象的属性,测试前需要简单介绍下rootObject,从名字理解就是根对象,Spring会从这个对象中读取属性或者调用对象的方法。

  获取数组中的值和设置新的值,设置使用setValue()方法,第一个参数是rootObject,第二个参数是新的值。

  列表,映射和数组是类似的就不单独解释了,示例如下:

  3、创建数组、映射和列表:创建映射和列表使用大括号开始和结尾,映射使用key:value形式,列表使用逗号分隔,他们都可以嵌套。

  4、方法调用:对于字符串可以直接调用字符串的方法,比如length()、size()方法等,对于rootObject可以直接调用它的方法。

  另一种方法调用,注册一个方法到StandardEvaluationContext上,registerFunction接受两个参数,第一个是方法名,第二个是Method对象,这种方法调用支持静态方法,表达式需要再前面加个#。

  5、关系运算、逻辑运算和算术运算:关系运算符有lt ('<'), gt ('>'), le ('<='), ge ('>='), eq ('=='), ne ('!='), div ('/'), mod ('%'), not ('!');逻辑运算符有and, or和not;算术运算有加减乘除和求余等。

  对于和'null'比较,null代表设么也没有,其他和null大于比较总返回true。

  6、获取类型和调用构造方法:获取类型使用T操作符,这个操作符也可以调用静态方法。对于在java.lang包中的类可以忽略包名,比如T(int),对于其他包需要使用全名T(java.util.Date)。

  7、Bean引用:需要在StandardEvaluationContext上设置BeanResolver的实例,BeanResolver意识就是从什么地方获取bean,在Spring项目上可以注入BeanFactory,在非Spring项目中可以自定义一个BeanResolver的实现。表达式中使用@字符获取bean。以下是一个Spring项目中获取bean的方式。

  自定义MyBeanResolver实现接口BeanResolver,并实现resolve方法。

  8、三元运算符和Elvis运算符:学过编程的都熟悉三元运算符,因为在很多语言中都存在,三元运算符使用类似if-then-else条件逻辑计算,在表达式中类似这样的"true ? 'trueExp' : 'falseExp'"。

  Elvis运算符在Groovy中使用,为了简化三元运算符,语法为'notnull'?:null

  9、安全的导航操作符:从一个对象中获取另一个对象并使用它属性的时候,为了防止NullPointerException会对每个对象判空,在Groovy语言中可以使用安全的导航避免空指针,语法如下:obj1?.obj2?.field。在Spring表达式中可以使用类似的语法。

  10、集合选择:从集合中选择一部分数据是比较常用的,在Java中可以循环对集合每个元素处理,在Spring 表达式中可以使用语法?[selectionExpression]获取全部符合条件的数据,使用^[selectionExpression]获取第一个元素,使用$[selectionExpression]获取最后一个元素。

  11、表达式模板:在一个字符串中可以使用#{ }在文本中使用表达式,在解析的方法中需要传递TemplateParserContext。

  在spring-expression-5.3.26.jar包中有个SpringExpresions.g文件,这个文件就是SpEL的语法文件,通过语法文件解析输入的表达式生成语法树,在语法正确的前提下计算表达式的值。

  

  spring的语法文件

  在类InternalSpelExpressionParser中处理输入的表达式,处理成TokenStream,最后生成一颗编译的语法树。

  

  解析表达式

  举报/反馈