Spring 表达式

Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”,类似于Struts2x中使用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等,并且能与Spring功能完美整合,如能用来配置Bean定义。 SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用。表达式语言给静态Java语言增加了动态功能。


SpEL 的工作原理

工作原理
1. 首先定义表达式:“1+2”;
2. 定义解析器ExpressionParser实现,SpEL提供默认实现SpelExpressionParser;
2.1. SpelExpressionParser解析器内部使用Tokenizer类进行词法分析,即把字符串流分析为记号流,记号在SpEL使用Token类来表示;
2.2. 有了记号流后,解析器便可根据记号流生成内部抽象语法树;在SpEL中语法树节点由SpelNode接口实现代表:如OpPlus表示加操作节点、IntLiteral表示int型字面量节点;使用SpelNodel实现组成了抽象语法树;
2.3. 对外提供Expression接口来简化表示抽象语法树,从而隐藏内部实现细节,并提供getValue简单方法用于获取表达式值;SpEL提供默认实现为SpelExpression;
3. 定义表达式上下文对象(可选),SpEL使用EvaluationContext接口表示上下文对象,用于设置根对象、自定义变量、自定义函数、类型转换器等,SpEL提供默认实现StandardEvaluationContext;
4. 使用表达式对象根据上下文对象(可选)求值(调用表达式对象的getValue方法)获得结果。

SpEL 的主要接口

ExpressionParser接口

表示解析器,默认实现是org.springframework.expression.spel.standard包中的SpelExpressionParser类,使用parseExpression方法将字符串表达式转换为Expression对象,对于ParserContext接口用于定义字符串表达式是不是模板,及模板开始与结束字符。

EvaluationContext接口

表示上下文环境,默认实现是org.springframework.expression.spel.support包中的StandardEvaluationContext类,使用setRootObject方法来设置根对象,使用setVariable方法来注册自定义变量,使用registerFunction来注册自定义函数等等。

Expression接口

表示表达式对象,默认实现是org.springframework.expression.spel.standard包中的SpelExpression,提供getValue方法用于获取表达式值,提供setValue方法用于设置对象值。

SpEL 的语法

使用SpEl进行表达式操作:

构建解析 : ExpressionParser parser = new SpelExpressionParser();
使用表达式进行解析 :Expression exp = parser.parseExpression( SpEl);
获取结果 : exp.getValue();

文本表达式

文本表达式支持字符表达式、日期、数字(正数、实数及十六进制数)、布尔类型及null.其中字符表达式需要用单引号声明。
对数字支持负数、指数及小数。默认情况下实数使用Double.parseDouble()进行表达式类型转换。

String[] lELs = { "'hello SpEL'", "1.028E+7", "0x12EF", "true", "null" };
assertEquals("hello SpEL",
        exp.parseExpression(lELs[0]).getValue(String.class));
assertEquals(new Double(10280000), exp.parseExpression(lELs[1])
        .getValue(Double.class));
assertEquals(new Integer(4847),
        exp.parseExpression(lELs[2]).getValue(Integer.class));
assertTrue(exp.parseExpression(lELs[3]).getValue(Boolean.class));
assertNull(exp.parseExpression(lELs[4]).getValue());
属性、数组、列表、字典(map)及索引

在表达式中访问类属性时直接使用属性名,属性名首字母大小写均可。
访问数组时可以使用[index]进行元素对象范围。
访问列表时,可直接使用类表的方法,通过点操作符

// 属性测试。time为SpElUtil类Date型数据,这里调用Date的属性Year
assertEquals(new Integer(2011), exp.parseExpression("time.Year + 1900")
        .getValue(secontext, Integer.class));
// 属性测试。innerClass为SpElUtil类中引入的其他类。
assertEquals(29,
        exp.parseExpression("innerClass.age").getValue(secontext));
// 设置SpElUtil类的numbers属性
spel.setNumbers(Arrays.asList(2, 3, 4, 5, 6, 7, 9));
// 访问对象属性数组通过索引
assertEquals(2, exp.parseExpression("numbers[0]").getValue(secontext));
// 访问map
assertEquals("string1",
        exp.parseExpression("maps[1]")
                .getValue(secontext, String.class));
内置列表

列表可以直接表示在表达式中使用{}符号表达。{}本身代表一个空的list

// 构造list
List<String> nums = (List<String>) exp.parseExpression(
        "{'a','b','c','d'}").getValue();
assertEquals(Arrays.asList("a", "b", "c", "d"), nums);
// 构造List<List<>>
List listOfLists = (List) exp.parseExpression("{ {1,2},{3,4} }")
        .getValue(secontext);
assertEquals(Arrays.asList(1, 2), listOfLists.get(0));
数组构造(spring3.0.3中会抛出异常)

可以通过熟悉的Java语法在表达是语言中定义。但目前不支持定义一个初始化的多维数组

// 创建没有初始值的数组
int[] a = (int[]) exp.parseExpression("new int[4]").getValue();
assertEquals(4, a.length);
// 创建带有初始值的数组
int[] b = (int[]) exp.parseExpression("new int[4]{1,2,3,4}").getValue();
assertEquals(3, b[2]);
// 创建二维数组
int[][] c = (int[][]) exp.parseExpression("new int[4][5]").getValue();
assertEquals(4, c.length);
assertEquals(5, c[0].length);
方法调用

表达式中的方法调用遵循java语法。

assertEquals("abC2def"
    ,exp.parseExpression("'abcdef'.replace('c','C2')")
        .getValue(String.class));
// 自定义类方法测试
assertFalse(exp.parseExpression("innerClass.isGt30ForAge()")
    .getValue(secontext, Boolean.class));
spel.getInnerClass().setAge(34);
assertTrue(exp.parseExpression("innerClass.isGt30ForAge()")
    .getValue(secontext, Boolean.class));
关系操作符

支持eq(“==”)、ne(“!=”)、le(“<=”)、lt(“<”)、gt(“>”)、ge(“>=”)、div(“/”)、mod(“%”)、not(“!”)、正则表达式及instanceof操作

assertTrue(exp.parseExpression("1 == 1").getValue(Boolean.class));
assertTrue(exp.parseExpression("1 eq 1").getValue(Boolean.class));
assertTrue(exp.parseExpression("1 > -1").getValue(Boolean.class));
assertTrue(exp.parseExpression("1 gt -1").getValue(Boolean.class));
assertTrue(exp.parseExpression("'a' < 'b'").getValue(Boolean.class));
assertTrue(exp.parseExpression("'a' lt 'b'").getValue(Boolean.class));
assertTrue(exp.parseExpression(
        " new Integer(123) instanceof T(Integer) ").getValue(
        Boolean.class));
assertTrue(exp.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'")
            .getValue(Boolean.class));
逻辑操作符

逻辑操作符支持and,or,not

assertTrue(exp.parseExpression("true and true").getValue(Boolean.class));
数学运算操作符

加法运算符可以用于数字,字符串和日期。减法可以用在数字和日期。乘法和除法只能用于对数字。其他受支持的数学运算是模数(%)和指数幂(^)。运行顺序按标准运算符优先级执行。
SpEL支持加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)运算。还提供求余(MOD)和除(DIV)而外两个运算符,与“%”和“/”等价,不区分大小写。

assertEquals(25.0, exp.parseExpression("1 + 2 * 8 div 4 mod 2 + 2 ^ 3 * 3e0") .getValue());
赋值操作

通过赋值操作进行属性设置。通常是调用setValue方法,但也可以在调用getValue时设置。

Date oldDate = spel.getTime();// 获取当前time属性值
exp.parseExpression("time").setValue(secontext, new Date(113, 2, 25)); // 为time属性重新赋值
Date newDate = spel.getTime();// 获取赋值后的time属性值
assertEquals(2013, exp.parseExpression("time.Year + 1900").getValue(secontext));
assertNotSame(oldDate, newDate);
// 或者使用下属方法赋值
assertEquals("abc",exp.parseExpression("Name = 'abc'").getValue(secontext));
类型

通过特殊的T操作符可以用来指定一个java.lang.Class的实例。在实例话对象的静态方法将会被调用。

Class dateClass = exp.parseExpression("T(java.util.Date)").getValue(Class.class);
assertEquals("java.util.Date", dateClass.getName());
assertTrue(exp.parseExpression(
                "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean.class));   
构造器

构造器通过new操作被调用。在new操作时需要指明类的完全类名(包括包路径)

SpelTestInnerClass spt = exp
        .parseExpression(
                "new  leeyee.study.spring3.bean.SpelTestInnerClass('constructTest',23)")
        .getValue(SpelTestInnerClass.class);
变量

变量可以通过 #变量名 在表达式中被引用。变量通过StandardEvaluationContext类的setVariable方法进行设置

List<Integer> list = new ArrayList<Integer>();
list.addAll(Arrays.asList(2, 3, 4, 5, 6, 7, 9));
secontext.setVariable("list", list);
List<Integer> vList = (List<Integer>) exp.parseExpression("#list")
        .getValue(secontext);
assertEquals(vList, list);
#this变量

变量#this被定义为当前操作对象的引用。

List<Integer> nums = (List<Integer>) exp.parseExpression(
        "#list.?[#this >5]").getValue(secontext); // 获取值大于5的元素集合
assertEquals(nums, Arrays.asList(6, 7, 9));
用户自定义函数

你可以扩展SpEL通过注册自定义函数。注册后的函数可以在表达式中通过其名称进行调用。函数的注册是通过StandardEvaluationContext类的registerFunction方法进行声明

context.registerFunction("len", SpElUtil.class.getDeclaredMethod("len",
        new Class[] { String.class }));
assertEquals(3, exp.parseExpression("#len('abc')").getValue(context));
三元操作
assertTrue(exp.parseExpression(" true ? true :false").getValue(
        Boolean.class));
Elvis操作

Elvis操作是一个短的三元操作符语法,通常在Groovy语言中使用。
Note: Elvis操作在表达式中可以用来生成默认值,当被访问属性为空时。比如@Value。

@Value("#systemPro['mail.port'] ? : 25}")
// 当mail.port为空时将默认为25
Expression ex = exp.parseExpression("name?:'name is null'");
assertEquals("override", ex.getValue(secontext, String.class));
spel.setName(null);
assertEquals("name is null", ex.getValue(secontext, String.class));
spel.setName("override");
安全导航操作

该操作是为避免空指针异常。他是来自Groovy语言的。典型的当你有一个指向对象的引用,在你访问其方法或属性时,可能需要验证该对象的方法或属性是否为空,为了避免验证,使用安全导航操作将简单的返回null而不是空指针异常。

assertEquals("innerClass", exp.parseExpression("innerClass?.name")
        .getValue(secontext, String.class));
spel.setInnerClass(null);
// 使用这种表达式可以避免抛出空指针异常
assertNull(exp.parseExpression("innerClass?.name").getValue(secontext,  String.class));
集合选择

选择是一个强大的表达式语言属性,可以使用选择表达式过滤源集合,从而生成一个新的符合选择条件的集合。
选择的语法为?[selectionExpression]。他将过滤集合并且返回一个新的集合(原集合的子集)。选择语句也可用在Map中,过滤keySet及valueSet分别使用key和value关键字。另外:选择语法中,选择符合条件的结果集的第一个元素的语法为 ^[selectionExpression],选择最后一个元素的语法为$[selectionExpression]

spel.setNumbers(Arrays.asList(2, 3, 4, 5, 6, 7, 9));
List<Integer> nums = (List<Integer>) exp.parseExpression(
        "numbers.?[#this >5]").getValue(secontext);
assertEquals(nums, Arrays.asList(6, 7, 9));
// 获取第一个元素
assertEquals(6,
        exp.parseExpression("numbers.^[#this > 5]").getValue(secontext));
// 获取最后一个元素
assertEquals(9,
        exp.parseExpression("numbers.$[#this > 5]").getValue(secontext));
Map<Integer, String> maps = (Map<Integer, String>) exp.parseExpression(
        "maps.?[value == 'string3' ]").getValue(secontext);
Map<Integer, String> tmap = new HashMap<Integer, String>();
tmap.put(3, "string3");
assertEquals(maps, tmap);
Map<Integer, String> mapk = (Map<Integer, String>) exp.parseExpression(
        "maps.?[key > 2 and key < 4 ]").getValue(secontext);
assertEquals(mapk, tmap);
集合投影

语法![projectionExpression]判断集合中每个元素是否符合语法要求

assertEquals(Arrays.asList(5, 6, 7, 8, 9), exp
        .parseExpression("numbers.![#this+3]").getValue(secontext));
模板表达式

表达式模板允许混合文字表达式,一个或多个值计算块。每一个值计算块被声明通过可被自定义的前缀和后缀,一般选择使用#{}作为一个定界符。

assertEquals(
        " this is a test 4",
        exp.parseExpression(" this is a test #{ maps.![key].get(3)}",
                new TemplateParserContext()).getValue(secontext,
                String.class));

SpEL 在Bean中的使用

xml风格的配置

SpEL支持在Bean定义时注入,默认使用“#{SpEL表达式}”表示,其中“#root”根对象默认可以认为是ApplicationContext,只有ApplicationContext实现默认支持SpEL,获取根对象属性其实是获取容器中的Bean。

<bean id="world" class="java.lang.String">  
    <constructor-arg value="#{' World!'}"/>  
</bean>  
<bean id="hello1" class="java.lang.String">  
    <constructor-arg value="#{'Hello'}#{world}"/>  
</bean>    
<bean id="hello2" class="java.lang.String">  
    <constructor-arg value="#{'Hello' + world}"/>  
    <!-- 不支持嵌套的 -->  
    <!--<constructor-arg value="#{'Hello'#{world}}"/>-->  
</bean>  
<bean id="hello3" class="java.lang.String">  
    <constructor-arg value="#{'Hello' + @world}"/>  
</bean>  

模板默认以前缀“#{”开头,以后缀“}”结尾,且不允许嵌套,如“#{‘Hello’#{world}}”错误,如“#{‘Hello’ + world}”中“world”默认解析为Bean。当然可以使用“@bean”引用了。

注解风格的配置

基于注解风格的SpEL配置也非常简单,使用@Value注解来指定SpEL表达式,该注解可以放到字段、方法及方法参数上。

<?xml version="1.0" encoding="UTF-8"?>  
<beans  xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
         xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation="  
          http://www.springframework.org/schema/beans  
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
          http://www.springframework.org/schema/context  
http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
   <context:annotation-config/>  
   <bean id="world" class="java.lang.String">  
       <constructor-arg value="#{' World!'}"/>  
   </bean>  
   <bean id="helloBean1" class="com.heqing.spring.=SpELBean"/>  
   <bean id="helloBean2" class="com.heqing.spring.=SpELBean"/>  
       <property name="value" value="haha"/>  
   </bean>  
</beans>  
import org.springframework.beans.factory.annotation.Value;  
public class SpELBean {  
    @Value("#{'Hello' + world}")  
    private String value;  
    //setter和getter由于篇幅省略,自己写上  
}  
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring表达式语言(SpEL)是一种强大的表达式语言,用于在运行时计算表达式的值。SpEL可以用于配置文件、注解和代码中,支持对Bean的属性、方法和构造函数进行访问和操作,提供了很多常用的运算符和函数,是Spring框架中非常重要的一部分。 SpEL支持以下特性: 1. 引用Bean的属性和方法。可以使用“#”符号引用Bean的属性和方法,如“#user.name”表示引用名为user的Bean的name属性。 2. 调用静态方法和常量。可以使用“T()”关键字调用静态方法和常量,如“T(java.lang.Math).PI”表示引用Math类的PI常量。 3. 访问数组和集合。可以使用“[]”符号访问数组和集合,如“list[0]”表示访问名为list的集合的第一个元素。 4. 进行算术运算和比较运算。SpEL支持常见的算术运算和比较运算符,如“+”、“-”、“*”、“/”、“%”、“==”、“!=”、“<”、“>”等。 5. 定义变量和使用占位符。可以使用“#{}”定义变量和使用占位符,如“#{T(System).currentTimeMillis()}”表示定义一个名为currentTimeMillis的变量并赋值为当前时间的毫秒数。 6. 调用Bean的构造函数。可以使用“new”关键字调用Bean的构造函数,如“new java.util.Date()”表示调用java.util.Date类的无参构造函数。 SpEL可以在Spring的XML配置文件、@Value注解以及SpEL表达式注解中使用,可以方便地实现复杂的条件判断和动态计算。例如,可以使用SpEL表达式注解来实现@Scheduled注解的cron表达式的动态计算,或者在XML配置文件中使用SpEL表达式来实现Bean之间的依赖注入和条件配置等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值