SpEL简介与功能特性

Spring表达式语言(简称SpEL)是一个支持查询并在运行时操纵一个对象图的功能强大的表达式语言。SpEL语言的语法类似于统一EL,但提供了更多的功能,最主要的是显式方法调用和基本字符串模板函数。

同很多可用的Java 表达式语言相比,例如OGNL,MVEL和JBoss EL,SpEL的诞生是为了给Spring社区提供一个可以给Spring目录中所有产品提供单一良好支持的表达式语言。其语言特性由Spring目录中的项目需求驱动,包括基于eclipse的SpringSource套件中的代码补全工具需求。也就是说,SpEL是一个基于技术中立的API允许需要时与其他表达式语言集成。

SpEL作为Spring目录中表达式求值的基础,它并不是直接依赖于Spring而是可以被独立使用。为了能够自包含,本章中的许多示例把SpEL作为一个独立的表达式语言来使用。这就需要创建一些如解析器的引导基础组件类。大多数Spring用户只需要为求值编写表达式字符串而不需要关心这些基础组件

SpEL功能特性:

  • ž 字符表达式
  • ž 布尔和关系操作符
  • ž 正则表达式
  • ž 类表达式
  • ž 访问properties,arrays,lists,maps
  • ž 方法调用
  • ž 关系操作符
  • ž 赋值
  • ž 调用构造器
  • ž 三元操作符
  • ž 变量
  • ž 用户自定义函数
  • ž 集合投影
  • ž 集合选择
  • ž 模板表达式

一、为什么需要Spring表达式语言

1.1、新建一个Maven Web项目,添加依赖,pom.xml如下所示:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId>
<artifactId>Spring053</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>Spring053</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
</dependencies>
</project>

表达式语言所依赖的包如下所示:

2.2、为了IOC,定义了用户类User.java与Order.java,如下所示:

用户类:

package com.zhangguo.Spring053.spel01;

/**
* 订单类
*
*/
public class Order {
/**
* 订单名称
*/
private String orderName;
/*
* 用户姓名
*/
private String userName;
/**
* 用户对象
*/
private User customer; public String getOrderName() {
return orderName;
} public void setOrderName(String orderName) {
this.orderName = orderName;
} public User getCustomer() {
return customer;
} public void setCustomer(User customer) {
this.customer = customer;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} @Override
public String toString() {
return "订单名:"+this.getOrderName()+",姓名:"+this.getUserName()+",编号:"+this.getCustomer().getId();
}
}

订单类:

package com.zhangguo.Spring053.spel01;

/**
* 订单类
*
*/
public class Order {
/**
* 订单名称
*/
private String orderName;
/*
* 用户姓名
*/
private String userName;
/**
* 用户对象
*/
private User customer; public String getOrderName() {
return orderName;
} public void setOrderName(String orderName) {
this.orderName = orderName;
} public User getCustomer() {
return customer;
} public void setCustomer(User customer) {
this.customer = customer;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
}
}

2.3、编写容器初始化的配置文件spel01.xml,内容如下:

<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="gyl" class="com.zhangguo.Spring053.spel01.User" p:id="9527">
<property name="name" value="郭永乐">
</property>
</bean> <bean id="order001" class="com.zhangguo.Spring053.spel01.Order">
<property name="customer" ref="gyl"></property>
<property name="name" value="#{gyl.name}"></property>
<property name="orderName" value='#{"Apples".toUpperCase()}'></property>

</bean> </beans>

在配置文件中,出现了#{}形式的表达式,我们就称为Spel表达式。#{gyl.name}作用是找到名称为gyl的bean取出中间的name值;#{"Apples".toUpperCase()}把字符串Apples转换成大写并输出。

2.4、取出bean测试

package com.zhangguo.Spring053.spel01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("spel01.xml");
Order order=ctx.getBean("order001",Order.class);
System.out.println(order);
}
}

2.5、运行结果

二、SpEL表达式Hello World!

Spring表达式语言(SpEL)从3.X开始支持,它是一种能够支持运行时查询和操作对象图的强大的表达式,其表达式语法类似于统一表达式语言。
SpEL支持如下表达式:
基本表达式:字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算、正则表达式、括号优先级表达式;
类相关表达式:类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用;
集合相关表达式:内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义;
其他表达式:模板表达式。

从一个Hello World!的示例开始:

package com.zhangguo.Spring053.spel02;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Test { public static void main(String[] args) {
//创建SpEL表达式的解析器
ExpressionParser parser=new SpelExpressionParser();
//解析表达式'Hello '+' World!'
Expression exp=parser.parseExpression("'Hello '+' World!'");
//取出解析结果
String result=exp.getValue().toString();
//输出结果
System.out.println(result);
}
}

运行结果:

Hello World!

从示例中可以看出java成功的将一个字符解析出了结果,如果我们把要解析的内容设置成1+1则会解析出2来,这里的1+1就是一个SpEL表达式,该表达式在java中只是一个String,通过解析可以得到字符串本身的意见,有点类型编译程序的感觉。

三、SpEL表达式

3.1、文字表达式

支持的文字表达的类型是字符串,日期,数值(整型,实型,和十六进制),布尔和空。字符串是由单引号分隔。使用反斜杠字符转移把一个单引号字符本身放在字符串中。

        ExpressionParser ep= new SpelExpressionParser();
System.out.println(ep.parseExpression("'HelloWorld'").getValue());
System.out.println(ep.parseExpression("0xffffff").getValue());
System.out.println(ep.parseExpression("1.234345e+3").getValue());
System.out.println(ep.parseExpression("new java.util.Date()").getValue());

运行结果:

HelloWorld
16777215
1234.345
Fri Jul 01 14:50:59 CST 2016

3.2、SPEL语言特性

3.2.1、属性

        //创建SpEL表达式的解析器
ExpressionParser parser=new SpelExpressionParser();
User user=new User(9527,"周星驰");
//解析表达式需要的上下文,解析时有一个默认的上下文
EvaluationContext ctx = new StandardEvaluationContext();
//在上下文中设置变量,变量名为user,内容为user对象
ctx.setVariable("user", user);
//从用户对象中获得id并+1900,获得解析后的值在ctx上下文中
int id = (Integer) parser.parseExpression("#user.getId() + 1900").getValue(ctx);
System.out.println(id);

运行结果:

11427

User类在前面已定义,这里增加了一个有参构造方法。上面的示例是调用方法,其实可以这样:引用对象属性,只需使用一个句点来表示一个嵌套的属性值,如下代码所示:

int id = (Integer) parser.parseExpression("#user.id + 1900").getValue(ctx);

运行结果:

11427

要注意的是此时#user后不再是一个方法而是.id,直接访问属性,在java中这样做是不行的,便SpEL中允许

3.2.2、数组

        String[] students=new String[]{"tom","jack","rose","mark","lucy"};
ctx.setVariable("students", students);
String student = parser.parseExpression("#students[3]").getValue(ctx,
String.class);
System.out.println(student);

结果:mark

3.2.3、列表

        List numbers = (List) parser.parseExpression("{1,2,3,4,5}").getValue();
System.out.println(numbers.get(2)+"");
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue();
System.out.println(((List)listOfLists.get(1)).get(1));

结果:3 y

3.2.4、索引器、与字典

        User user1=new User(9001,"邹任飞");
User user2=new User(9002,"练汶峰");
List<User> users=new ArrayList<User>();
users.add(user1);
users.add(user2);
ctx.setVariable("users", users);
String name = parser.parseExpression("#users[1].name").getValue(ctx,String.class);
System.out.println(name);

结果:练汶峰

在java中访问集合中的对象通过get(索引)而在SpEL中我们可以直接像数组那样访问对象。如果是一个字典可以这样:#users["tom"].id

3.2.5、方法

方法调用使用典型的Java编程语法。 你可能 还在文字调用方法。 也支持可变参数。

        String c = parser.parseExpression("'abcdef'.substring(2, 3)").getValue(String.class);
System.out.println(c);

结果:c

3.2.6、操作符

关系操作符:使用标准的操作符号支持关系操作符:等于,不等于,小于,小于等于,大于,大于等于。
逻辑操作符:支持的逻辑操作符包括and,or和not(!),不支持&&和||。
算术操作符:加法运算符可以用于数字,字符串和日期。减法可用于数字和日期。乘法和除法仅可以用于。其他支持的数学运算包括取模(%)和指数幂(^)。使用标准的运算符优先级。

关系运算符:

        //true
boolean trueValue1 = parser.parseExpression("2 == 2").getValue(Boolean.class);
//false
boolean falseValue1 = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
//true
boolean trueValue2 = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
//false,字符xyz是否为int类型
boolean falseValue2 = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
//true,正则是否匹配
boolean trueValue3 =parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
//false
boolean falseValue3=parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

逻辑运算:

        // -- AND 与运算 --
//false
boolean falseValue4 = parser.parseExpression("true and false").getValue(Boolean.class);
//true,isMember方法用于测试是否为某个对象的成员
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue4 = parser.parseExpression(expression).getValue(Boolean.class);
// -- OR 或运算--
//true
boolean trueValue5 = parser.parseExpression("true or false").getValue(Boolean.class);
//true
String expression1 = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue6 = parser.parseExpression(expression).getValue( Boolean.class);
//false
boolean falseValue5 = parser.parseExpression("!true").getValue(Boolean.class);
//false
String expression2 = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue6 = parser.parseExpression(expression).getValue(Boolean.class);

算术运算:

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

3.2.7、表达式支持定义bean、基于XML的配置

在xml配置中可以自由的使用SpEL,如下所示:

<bean id="gyl" class="com.zhangguo.Spring053.spel01.User" p:id="9527">
<property name="name" value="郭永乐">
</property>
</bean> <bean id="order001" class="com.zhangguo.Spring053.spel01.Order">
<property name="customer" ref="gyl"></property>
<property name="userName" value="#{gyl.name}"></property>
<property name="orderName" value='#{"Apples".toUpperCase()}'></property>
</bean> <bean id="numberGuess" class="org.spring.samples.NumberGuess">
<!--调用静态方法random() -->
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }" />
</bean> <bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
<property name="defaultLocale" value="#{ systemProperties['user.region'] }" />
</bean> <bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }" />
</bean>
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }" />
</bean>

3.2.8、表达式支持定义bean、基于注解的配置

用户类User.java:

package com.zhangguo.Spring053.spel03;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; /**
* 用户类
*/
@Component("user1")
public class User {
/**
* 编号
*/
@Value("#{9527+100}")
private int id;
/**
* 姓名
*/
@Value("#{'Hello'.toUpperCase()}")
private String name; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

订单类:Order.java

package com.zhangguo.Spring053.spel03;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository; /**
* 订单类
*
*/
@Repository("Order03")
public class Order {
/**
* 订单名称
*/
@Value("#{'Apple订单'}")
private String orderName;
/*
* 用户姓名
*/
@Value("#{user1.name}")
private String userName;
/**
* 用户对象
*/
@Value("#{user1}")
private User customer; public String getOrderName() {
return orderName;
} public void setOrderName(String orderName) {
this.orderName = orderName;
} public User getCustomer() {
return customer;
} public void setCustomer(User customer) {
this.customer = customer;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} @Override
public String toString() {
return "订单名:"+this.getOrderName()+",姓名:"+this.getUserName()+",编号:"+this.getCustomer().getId();
}
}

测试代码Test.java

package com.zhangguo.Spring053.spel03;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zhangguo.Spring053.spel03.User; public class Test { public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("spel03.xml");
User user1=ctx.getBean("user1",User.class);
System.out.println(user1.getId()+","+user1.getName()); Order Order03=ctx.getBean("Order03",Order.class);
System.out.println(Order03);
}
}

运行结果:

9627,HELLO
订单名:Apple订单,姓名:HELLO,编号:9627

3.2.9、操作符

        ExpressionParser ep = new SpelExpressionParser();
// 关系操作符
System.out.println(ep.parseExpression("5>2").getValue());
System.out.println(ep.parseExpression("2 between {1,9}").getValue());
// 逻辑运算符
System.out.println(ep.parseExpression("(5>2) and (2==1)").getValue());
// 算术操作符
System.out.println(ep.parseExpression("100-2^2").getValue());

结果:

true
true
false
96

3.2.10、变量与赋值

变量:变量可以在表达式中使用语法#’变量名’引用
ExpressionParser ep= new SpelExpressionParser();
//创建上下文变量
EvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable(“name”, “Hello”);
System.out.println(ep.parseExpression("#name").getValue(ctx));

输出:Hello
赋值:属性设置是通过使用赋值运算符。这通常是在调用setValue中执行但也可以在调用getValue内,也可通过”#varName=value”的形式给变量赋值。
System.out.println(ep.parseExpression("#name='Ryo'").getValue(ctx));

输出:Ryo

四、示例下载

点击下载

Spring学习总结(四)——表达式语言 Spring Expression Language的更多相关文章

  1. 应用于Java中的一个开源的表达式语言(Expression Language)

    OGNL(英文全称:Object Graph Navigation Language,中文名:对象导航图语言)是应用于Java中的一个开源的表达式语言(Expression Language),它被集 ...

  2. Spring学习记录(七)---表达式语言-SpEL

    SpEL---Spring Expression Language:是一个支持运行时查询和操作对象图表达式语言.使用#{...}作为定界符,为bean属性动态赋值提供了便利. ①对于普通的赋值,用Sp ...

  3. Spring学习(四)——使用Spring JDBC访问数据库

    本篇我们将在上一篇http://www.cnblogs.com/wenjingu/p/3824294.html的Demo程序的基础上增加数据持久层和业务层,实现登录验证功能. 1.修改gradle文件 ...

  4. JavaWeb框架SSH_Struts2_(四)----->表达式语言OGNL

    1. 表达式语言OGNL OGNL简介 OGNL基本语法 常量 操作符 OGNL表达式 OGNL基础 OGNL上下文 OGNL值栈 OGNL的访问 2. 具体内容 2.1 OGNL简介 OGNL(Ob ...

  5. JavaWeb框架_Struts2_(四)----->表达式语言OGNL

      2. 表达式语言OGNL 2.1 OGNL简介 OGNL(Object-Graph Navigation Language)对象图导航语言的缩写,OGNL是一种表达式语言(Expression L ...

  6. Spring3系列6 - Spring 表达式语言(Spring EL)

    Spring3系列6-Spring 表达式语言(Spring EL) 本篇讲述了Spring Expression Language —— 即Spring3中功能丰富强大的表达式语言,简称SpEL.S ...

  7. spring 学习(四): spring 的 jdbcTemplate 操作

    spring 学习(四): spring 的 jdbcTemplate 操作 spring 针对 javaee 的每一层,都提供了相应的解决技术,jdbcTemplate 的主要操作在 dao 层. ...

  8. Spring学习总结(一)——Spring实现IoC的多种方式

    控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法.没有IoC的程序中我们使用面向对象编程对象的创 ...

  9. EnjoyingSoft之Mule ESB开发教程第四篇:Mule Expression Language - MEL表达式

    目录 1. MEL的优势 2. MEL的使用场景 3. MEL的示例 4. MEL的上下文对象 5. MEL的Variable 6. MEL访问属性 7. MEL操作符 本篇主要介绍Mule表达式语言 ...

  10. spring 学习(五):spring 事务

    spring 学习(五):spring 事务 事务概要 一个数据库事务通常包含了一个序列的对数据库的读/写操作.它的存在包含有以下两个目的: 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同 ...

随机推荐

  1. 在Windows用Rebar来构建,编译,测试,发布Erlang项目

    rebar是一个遵循 Erlang/OTP 原则的 Erlang 项目构建工具,使用它可以减少构建标准 Erlang/OTP 项目架构配置的工作量,并且可以很容易的编译.测试.发布 Erlang 应用 ...

  2. 12G服务器在BIOS中收集阵列卡日志(TTY日志)的方法

      如果系统进不去.请参考如下方法收集日志. 请准备个U 盘,容量在8G以下(含8G),否则会识别不到. 图片参考,以描述为准 F2 enter BIOS option--> Enter the ...

  3. C# 轻量级ORM 编写思维

    目标: 降低研发人员门槛,提高效率,去除重复引用DLL的工作,基础配置由抽象工厂处理. 基础扩展 /// <summary> /// DataTable 转换为List 集合 /// &l ...

  4. 用c#开发微信 系列汇总

    网上开发微信开发的教程很多,但c#相对较少.这里列出了我所有c#开发微信的文章,方便自己随时查阅.   一.基础知识 用c#开发微信(1)服务号的服务器配置和企业号的回调模式 - url接入 (源码下 ...

  5. 《BI深入浅出》笔记

    今年的项目涉及到BI的知识点,读了<商业智能深入浅出>,这本书是基于IBM的产品做的,基础知识部分讲的非常成体系.记下来做个备忘: 1. BI简介 1.1 实施方案 1)项目规划: 2)系 ...

  6. 设计模式之美:Role Object(角色对象)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Role Object 的示例实现. 意图 通过明确地附加角色对象到目标对象中,以使对象可以适配不同的客户需求.每个角色对象都代 ...

  7. Json序列化之.NET开源类库Newtonsoft.Json的研究

     一.Json简介 JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的文 ...

  8. Unity3D手游开发实践

    <腾讯桌球:客户端总结> 本次分享总结,起源于腾讯桌球项目,但是不仅仅限于项目本身.虽然基于Unity3D,很多东西同样适用于Cocos.本文从以下10大点进行阐述: 架构设计 原生插件/ ...

  9. [nRF51822] 4、 图解nRF51 SDK中的Schedule handling library 和Timer library

    :nRF51822虽然是一个小型的单片机,但是能真正达到任意调用其官方驱动以及BLE协议栈的人还是奇缺的.据我所见,大都拿官方给的一个冗长的蓝牙低功耗心率计工程改的.之前我对于这个工程进行log跟踪, ...

  10. Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler

    上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK>我们讲述了如何使用Senparc.Weixin.MP SDK ...