加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Spring学习--Spring表达式语言(一)

(2014-05-13 21:53:44)
标签:

spring

投影

spel

详解

表达式

分类: Spring学习
1. Spring的表达式语言概述
   Spring表达式语言即Spring Expression Language ,简称"SpEL";类似OGNL(Struts2中的表达式语言)表达式,它能够构建复杂的表达式,对象图形导航,对象方法调用等.并且能够与Spring的功能完美结合.
   SpEL表达式给静态的Java代码添加了动态的功能.SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用。
2. SpEL表达式的功能
   最简单,最明显的就是该表达式的形式简单,可以减少我们的工作量.
3. SpEL表达式支持的表达式
   1. 基本表达式:字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式;
   2. 类相关表达式:类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用;
   3. 集合相关表达式:内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义;
   注意:SpEL表达式中不区分大小写.
   小例子:
package com.java.test;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionTest {
    @Test
    public void testexpression_r() {
        ExpressionParser parser = new SpelExpressionParser();//创建SpEL解析器,而且SpelExpressionParser为默认实现
        Expression e = parser.parseexpression_r("('Hello' + ' World').concat(#variable)");//创建表达式Expression对象
        EvaluationContext context = new StandardEvaluationContext();//构造上下文,比如:变量定义
        context.setVariable("variable", "!");//设置变量的值
        Assert.assertEquals("Hello World!", e.getValue(context));//求值,通过getValue获取指定的值
    }
}
2. SpEL表达式的基本执行原理
    1. 基本概念:
       1. 表达式:SpEL的核心就是表达式,所有SpEL都是围绕表达式来进行的;
       2. 解析器:用于将字符串表达式解析为表达式对象;
       3. 上下文:表达式所执行的环境,该环境可能定义变量、定义自定义函数、提供类型转换等等;(一般在Spring中几乎所有的Context都是与环境有关的);
       4. 根对象是默认的活动上下文对象:活动上下文对象表示了当前表达式操作的对象;
    2. 执行原理:
       如图所示:
       http://s12/mw690/003x41Zity6IN7gByJRdb&690
     1. 创建一个表达式;
     2. 创建解析器对象,SpELExpressionParser是默认的解析器对象的实现,
        2.1. 进行词法分析,SpelExpressionParser解析器内部使用Tokenizer类进行词法分析,即把字符串流分析为记号流(内部实现);
        2.2.进行表达式的语法分析,判断是否有语法错误,解析器便可根据记号流生成内部抽象语法树;在SpEL中语法树节点由SpelNode接口实现代表;
        2.3. 创建Expression对象来封装内部实现;
     3. 定义表达式上下文对象,也就是表达式的运行环境,SpEL提供默认实现StandardEvaluationContext;
     4. 求值获取结果,使用表达式对象根据上下文对象求值(调用表达式对象的getValue方法)获得结果;
3. 接口详解
    1. ExpressionParser接口
       解析器类,默认实现:SpelExpressionParser类,它可以使用parserExpression方法将字符串表达式转为Expression对象,对于 ParserContext接口用于定义字符串表达式是不是模板,及模板开始与结束字符;
     ExpressionParser代码:
     public interface ExpressionParser { 
        Expression parseexpression_r(String expressionString); 
        Expression parseexpression_r(String expressionString, ParserContext context); 
    
    小例子:
package com.java.test;
import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class ExpressionTest2 {
    @Test
    public void testexpression_r() {
        ExpressionParser ep = new SpelExpressionParser();
        ParserContext pc = new ParserContext() {
            @Override
            public boolean isTemplate() {
                // 判断是否是模板
                return true;
            }
            @Override
            public String getExpressionPrefix() {
                // 获取前缀
                return "#{";
           
            @Override
            public String getExpressionSuffix() {
                // 获取后缀
                return "}";
            }
        };
        String template = "#{'hello'}#{'world!'}";
        Expression e = ep.parseexpression_r(template, pc);
        System.out.println(e.getValue());
        Assert.assertEquals("helloworld!", e.getValue());
    }
}
2. EvaluationContext接口
   表示上下文环境,其默认实现是:StandardEvaluationContext类,使用setRootObject方法来设置根对象,使用setVariable方法来注册自定义变量,使用registerFunction来注册自定义函数等等。
3. Expression 接口
   表达式对象,默认实现是:SpelExpression类;提供getValue方法用于获取表达式值,提供setValue方法用于设置对象值;
4.SpEL语法
    1. 基本表达式
       1. 字面量表达式
          SpEL所支持的字面量表达式包括:字符串(String)、数字类型(int、long、float、double)、布尔类型(Boolean)、null类型。
          例如:String类型: String str =ep.parseexpression_r("helloworld").getValue(String.class);
               int 类型:  int i = ep.parseexpression_r("1").getValue(int.class);
               long类型:  long l = ep.parseexpression_r("1L").getValue(long.class);
               float类型: float f = ep.parseexpression_r("1.1").getValue(float.class);
               double类型:double d = ep.parseexpression_r("1.222").getValue(double.class);
               boolean类型: boolean b = ep.parseexpression_r("true").getValue(boolean.class);
               null类型:  Object o = ep.parseexpression_r("1.222").getValue(Object.class);
        2. 算术表达式
           所谓的算术表达式,我们并不陌生,就是一些基本的加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)运算.
           例如:"+":int i = ep.parseexpression_r("1+1").getValue(int.class);
                "-":int i = ep.parseexpression_r("1-1").getValue(int.class);
                "*":int i = ep.parseexpression_r("1*1").getValue(int.class);
                "/":int i = ep.parseexpression_r("1/1").getValue(int.class);
                "%"求余数:int i = ep.parseexpression_r("1%1").getValue(int.class);
                "^"幂运算:int i = ep.parseexpression_r("1^1").getValue(int.class);
         3. 关系表达式
            SpEL支持的关系表达式有:
                基本的:等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)、小于等于(<=)
                区间范围的:between例如: int i = ep.parseexpression_r("1
between{1,2}").getValue(int.class);  between运算符右边操作数必须是列表类型,且只能包含2个元素。第一个元素为开始,第二个元素为结束,区间运算是包含边界值的,即 xxx>=list.get(0) && xxx<=list.get(1)。
                一些简写形式: “EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”来表示等于、不等于、大于、大于等于、小于、小于等于,不区分大小写。
          4. 逻辑表达式
              SpEL包括:且(and)、或(or)、非(!或NOT),但是不支持"&&"和"||".
              例如:boolean b = ep.parseexpression_r("1>2 and 3>2").getValue(boolean.class);
          5. 操作字符串:
              拼接字符串:在SpEL中可以使用"+"号来进行字符串的拼接;
              截取字符:使用String[index],例如:"'hello'[0]",返回的就是h了.记得[0]要写在单引号外面.
          6.三目运算符和Elivis运算表达式
              三目运算符:"表达式1?表达式2:表达式3",例如:2>1 ?true:false;
              Elivis运算符“表达式1?:表达式2从 Groovy语言引入用于简化三目运算符的,当表达式1为非null时则返回表达式1,当表达式1为null时则返回表达式2,简化了三目运算符方式“表 达式1? 表达式1:表达式2”,例如:boolean str=ep.parseexpression_r("null?:false").getValue(boolean.class);
          7.正则表达式:
              语法: "将要处理的字符串" matches "处理规则";
                    例如:boolean str=ep.parseexpression_r("'123' matches '\\d{3}'").getValue(boolean.class);
      2. 类相关表达式

            1、类类型表达式使用“T(Type)”来表示java.lang.Class实例,“Type”必须是类全限定名,“java.lang”包除外,即该包下的类可以不指定包名;使用类类型表达式还可以进行访问类静态方法及类静态字段。 例如:Class str=ep.parseexpression_r("T(String)").getValue(Class.class);

                int i=ep.parseexpression_r("T(Integer).MAX_VALUE").getValue(int.class);

             注意: 对于java.lang包里的可以直接使用“T(String)”访问;其他包必须是类全限定名;

           2. 类实例化:类实例化同样使用java关键字“new”,类名必须是全限定名,但java.lang包内的类型除外,如String、Integer。

                例如: String date=ep.parseexpression_r("new java.util.Date()").getValue(String.class);

                        String str=ep.parseexpression_r("new String('fff')").getValue(String.class);

            3. instanceof表达式:SpEL支持instanceof运算符,跟Java内使用同义;如“'haha' instanceof T(String)”将返回true。

            4. 变量定义及引用:变量定义通过EvaluationContext接口的setVariable(variableName, value)方法定义;在表达式中使用“#variableName”引用;除了引用自定义变量,SpE还允许引用根对象及当前上下文对象,使用 “#root”引用根对象,使用“#this”引用当前上下文对象(内置对象);

             小例子:

package com.java.test;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionTest3 {
    @Test
    public void testexpression_r() {
        ExpressionParser ep = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
        // 创建对象
        ec.setVariable("aaa", "fff");//创建对象
        String variable = ep.parseexpression_r("#aaa").getValue(ec, String.class);//获取对象值
        ec = new StandardEvaluationContext("eee");//设置根对象
        String variable2 = ep.parseexpression_r("#root").getValue(ec, String.class);
        String variable3 = ep.parseexpression_r("#this").getValue(ec, String.class);
        System.out.println(variable);
        System.out.println(variable2);
        System.out.println(variable3);
    }
}

使用“#variable”来引用在EvaluationContext定义的变量;除了可以引用自定义变量,还可以使用“#root”引用根对 象,“#this”引用当前上下文对象,此处“#this”即根对象。

            5. 自定义函数目前只支持类静态方法注册为自定义函数;SpEL使用StandardEvaluationContext的registerFunction方法进行注册自定义函数,其实完全可以使用setVariable代替,两者其实本质是一样的;

                小例子(该例子中的parseInt方法为静态类中的已存在的方法):

package com.java.test;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class MyFunction {
    @Test
    public void testFunciton() {
        ExpressionParser ep=new SpelExpressionParser();
        StandardEvaluationContext ec=new StandardEvaluationContext();
        try {
            Method me=Integer.class.getDeclaredMethod("parseInt",String.class);
            ec.registerFunction("parseInt", me);
            ec.setVariable("parseInt2",me);
            String func="#parseInt('3')==#parseInt2('3')";
            boolean b=ep.parseexpression_r(func).getValue(ec,boolean.class);
            System.out.println(b);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

            6.  赋值表达式  

             SpEL即允许给自定义变量赋值,也允许给跟对象赋值,直接使用“#variableName=value”即可赋值;

            小例子:

package com.java.test;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionTest4 {
    @Test
    public void testexpression_r() {
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
        Expression e = parser.parseexpression_r("#root='aaa'");//将aaa的值赋给#root
        Assert.assertEquals("aaa", e.getValue(ec));
    }
}
7. 对象属性存取及安全导航表达式对 象属性获取非常简单,即使用如“a.property.property”这种点缀式获取,SpEL对于属性名首字母是不区分大小写的;SpEL还引入了 Groovy语言中的安全导航运算符“(对象|属性)?.属性”,用来避免但“?.”前边的表达式为null时抛出空指针异常,而是返回null;修改对象属性值则  可以通过赋值表达式或Expression接口的setValue方法修改。
小例子:
package com.java.test;
import java.util.Date;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionTest5 {
    @Test
    public void testexpression_r() {
        ExpressionParser ep = new SpelExpressionParser();
        Date date = new Date();
        EvaluationContext ec = new StandardEvaluationContext(date);
        int year = ep.parseexpression_r("year").getValue(ec, int.class);// 对于当前上下文对象属性及方法访问,可以直接使用属性或方法名访问,不区分大小写
        int year2 = ep.parseexpression_r("#root?.year").getValue(ec, int.class);//安全访问方式
        int year3 = ep.parseexpression_r("year=1").getValue(ec, int.class);//给根对象赋值
        ep.parseexpression_r("year").setValue(ec, "3".toString());
        String year4 = ep.parseexpression_r("year").getValue(ec, String.class);// 也可以使用setValue的方式访问
        System.out.println(year);
    }
}
SpEL引入了Groovy的安全导航运算符,如果此处根对象为null,那么访问其属性时肯定抛出空指针异常,而采用“?.”安全访问导航运算符将不抛空指针异常,而是简单的返回null。
8.对象方法的调用.
    对于根方法可以直接调用.
    例如:int year2 = ep.parseexpression_r("getYear()").getValue(ec, int.class);
9.Bean引用SpEL支持使用“@”符号来引用Bean,在引用Bean时需要使用BeanResolver接口实现来查找Bean,Spring提供BeanFactoryResolver实现
例如:
  ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
        ctx.refresh();
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(new BeanFactoryResolver(ctx));
        Properties result1 = parser.parseexpression_r("@systemProperties")
                .getValue(context, Properties.class);
        Assert.assertEquals(System.getProperties(), result1);
10.集合相关表达式
   1.内联List 在SpEL 中{1,2,3}将返回一个整型的ArrayList,而“{}”将返回空的List,对于字面量表达式列表,SpEL会使用java.util.Collections.unmodifiableList方法将列表设置为不可修改。
小例子:
package com.java.test;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionList {
    @Test
    public void testList() {
        ExpressionParser ep = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
       //字面量表达式
        // List e = (List) ep.parseexpression_r("{1,2,3}").getValue(ec);
        // e.set(0, "fff"); // 对于列表中的数据不可修改
        // System.out.println(e.get(0));
       //变量表达式
        // String expression = "{{1+1},{2+1}}";
        // List li = (List) ep.parseexpression_r(expression).getValue(ec);
        // li.set(0, 4);// 只要list中有一个不是字面量表达式,那么就返回原始的list,并且可以修改值
        // System.out.println(li.get(0));
       //数组
        // String expression = "new int[2]";
        // int[] arr = (int[]) ep.parseexpression_r(expression).getValue(ec);
        // arr[0] = 1;
        // arr[1] = 2;
        // System.out.println(arr.length);
       //二维数组
        // String expression = "new int[2][2]";
        // int[][] arr = (int[][]) ep.parseexpression_r(expression).getValue(ec);
        // arr[0][0] = 1;
        // arr[0][1] = 2;
        // arr[1][0] = 3;
        // arr[1][1] = 4;
        // System.out.println(arr.length);
       //
        // String expression = "new int[1][2][3]";
        // int[][][] arr = (int[][][])
        // ep.parseexpression_r(expression).getValue(ec);
    }
}
该例子中,把基本的list的类型都写在注释里面了.
    2. 集合,Map元素访问:SpEL目前支持所有集合类型和字典类型的元素访问,使用“集合[索引]”访问集合元素,使用“map[key]”访问字典元素;
看下面的这个例子,例子中有注释:
package com.java.test;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionMap {
    @Test
    public void testList() {
        ExpressionParser ep = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
       //获取值
        // String expression = "{1,2,3}[0]";// 由于返回的是集合的一个值,所以不能用集合类型来接收
        // int li = (Integer) ep.parseexpression_r(expression).getValue(ec);
        // System.out.println(li);
       //获取集合的值
        // Collection c = new HashSet();
        // c.add(1);
        // c.add(2);
        // ec.setVariable("c", c);// 设置变量
        // int i = (Integer) ep.parseexpression_r("#c[0]").getValue(ec);// 获取值
        // System.out.println(i);
       //获取Map的值
        // Map map = new HashMap();
        // map.put("1", "hello");
        // map.put("2", "world");
        // ec.setVariable("map", map);
        // String str = (String) ep.parseexpression_r("#map['1']").getValue(ec);//
        // 直接通过key获取指定的value
        // System.out.println(str);
    }
}
    3. 列表,字典,数组元素修改可以使用赋值表达式或Expression接口的setValue方法修改;
  小例子:
package com.java.test;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ExpressionListChange {
    @Test
    public void testList() {
        ExpressionParser ep = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
       //修改数组的值
        // int[] arr = new int[] { 1, 2 };
        // ec.setVariable("arr", arr);
        // int i = ep.parseexpression_r("#arr[0]=999").getValue(ec, int.class);
        // System.out.println(i);
       //修改集合的值
        // Collection c = new ArrayList();
        // c.add(1);
        // c.add(2);
        // c.add(3);
        // ec.setVariable("c", c);
        // int i = ep.parseexpression_r("#c[1] = 1").getValue(ec, int.class);
        // ep.parseexpression_r("#c[2]").setValue(ec, 555);
        // int j = ep.parseexpression_r("#c[2]").getValue(ec, int.class);
        // System.out.println(i);
        // System.out.println(j);
       //修改Map的值
        Map map = new HashMap();
        map.put("1", "hello");
        map.put("2", "world");
        ec.setVariable("map", map);
        ep.parseexpression_r("#map['1']").setValue(ec, "haha");
        String str = ep.parseexpression_r("#map['1']").getValue(ec, String.class);
        System.out.println(str);
    }
}
     4. 集合投影
         所谓集合投影,在SpEL中指的是根据集合中的元素中通过选择来构造另一个集合,该集合和原集合具有相同数量的元素,SpEL使用“(list|map).![投影表达式]”来进行投影运算;
        小例子:
        package com.java.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class CreateListTest {
    @Test
    public void testCreateList() {
        ExpressionParser ep = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
        Collection c = new ArrayList();
        c.add(1);
        c.add(2);
        ec.setVariable("c", c);
        Collection coll = ep.parseexpression_r("#c.![#this+1]")
                .getValue(ec, Collection.class);
// 创建了一个新的集合,投影表达式中“#this”代表每个集合或数组元素,
//可以使用比如“#this.property”来获取集合元素的属性
        System.out.println(coll.size());// 输出集合的个数
        Map map = new HashMap();
        map.put("1", "hello");
        map.put("2", "world");
        ec.setVariable("map", map);
        List ma = ep.parseexpression_r("#map.![value+1]").getValue(ec,
                List.class);
// Map投影最终只能得到List结果,如上所示,对于投影表达式中的
//“#this”将是Map.Entry,所以可以使用“value”来获取值,使用//“key”来获取键
        System.out.println(ma.size());
    }
}
    5. 集合选择
        SpEL中的集合选择指的是:在SpEL指根据原集合通过条件表达式选择出满足条件的元素并构造为新的集合,SpEL使用 “(list|map).?[选择表达式]”,其中选择表达式结果必须是boolean类型,如果true则选择的元素将添加到新集合中,false将不添加到新集合中;
小例子:
package com.java.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class ChoiceList {
    @Test
    public void testChoiceList() {
        ExpressionParser ep = new SpelExpressionParser();
        EvaluationContext ec = new StandardEvaluationContext();
        Collection c = new ArrayList();
        c.add(1);
        c.add(11);
        ec.setVariable("c", c);
        Collection coll = ep.parseexpression_r("#c.![#this>5]")
                .getValue(ec, Collection.class);// #this>4表示集合中的元素的值是否比5大,如果大,返回true,否则返回false
        System.out.println(coll);
        Map map = new HashMap();
        map.put("1", "hello");
        map.put("2", "world");
        ec.setVariable("map", map);
        Map ma = ep.parseexpression_r("#map.?[#this.key!='1']")
                .getValue(ec, Map.class);//#this.key获取到Map中的 key值,去除键值不等于的value
        List li = ep.parseexpression_r("#map.?[key!='1'].![value+1]")
                .getValue(ec, List.class);
        System.out.println(ma.size());
        System.out.println(li.size());
    }
}
如“#map.?[#this.key != 'a']”将选择键值不等于”a”的,其中选择表达式中“#this”是Map.Entry类型,而最终结果还是Map,这点和投影不同;集合选择和投影 可以一起使用,如“#map.?[key != 'a'].![value+1]”将首先选择键值不等于”a”的,然后在选出的Map中再进行“value+1”的投影;

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有