使⽤springEL表达式完成动态配置(⼀)
Spel概述
Spring表达式语⾔全称为“Spring Expression Language”,缩写为“SpEL”,类似于Struts2x中使⽤的OGNL表达式语⾔,能在运⾏时构建复杂表达式、存取对象图属性、对象⽅法调⽤等等,并且能与Spring功能完美整合,如能⽤来配置Bean定义。
表达式语⾔给静态Java语⾔增加了动态功能。
SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使⽤。
Spel能⼲什么?
表达式语⾔⼀般是⽤最简单的形式完成最主要的⼯作,减少我们的⼯作量。
SpEL⽀持如下表达式:
⼀、基本表达式: 字⾯量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三⽬运算及Elivis表达式、正则表达式、括号优先级表达式;
⼆、类相关表达式: 类类型表达式、类实例化、instanceof表达式、变量定义及引⽤、赋值表达式、⾃定义函数、对象属性存取及安全导航表达式、对象⽅法调⽤、Bean引⽤;
三、集合相关表达式: 内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不⽀持多维内联数组初始化;不⽀持内联字典定义;
四、其他表达式:模板表达式。
注:SpEL表达式中的关键字是不区分⼤⼩写的。
SpEL基础
HelloWorld
⾸先准备⽀持SpEL的Jar包:“pression-3.0.5.RELEASE.jar”将其添加到类路径中。
SpEL在求表达式值时⼀般分为四步,其中第三步可选:⾸先构造⼀个解析器,其次解析器解析字符串表达式,在此构造上下⽂,最后根据上下⽂得到表达式运算后的值。
让我们看下代码⽚段吧:
package com.javacode2018.spel;
import org.junit.Test;
import pression.EvaluationContext;
import pression.Expression;
import pression.ExpressionParser;
import pression.spel.standard.SpelExpressionParser;
el表达式获取值import pression.spel.support.StandardEvaluationContext;
public class SpelTest {
@Test
public void test1() {
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("end", "!");
System.out.Value(context));
}
}
输出
Hello World!
接下来让我们分析下代码:
1)创建解析器:SpEL使⽤ExpressionParser接⼝表⽰解析器,提供SpelExpressionParser默认实现;
2)解析表达式:使⽤ExpressionParser的parseExpression来解析相应的表达式为Expression对象。
3)构造上下⽂:准备⽐如变量定义等等表达式需要的上下⽂数据。
4)求值:通过Expression接⼝的getValue⽅法根据上下⽂获得表达式值。
是不是很简单,接下来让我们看下其具体实现及原理吧。
SpEL原理及接⼝
SpEL提供简单的接⼝从⽽简化⽤户使⽤,在介绍原理前让我们学习下⼏个概念:
⼀、表达式: 表达式是表达式语⾔的核⼼,所以表达式语⾔都是围绕表达式进⾏的,从我们⾓度来看是“⼲什么”;
⼆、解析器: ⽤于将字符串表达式解析为表达式对象,从我们⾓度来看是“谁来⼲”;
三、上下⽂: 表达式对象执⾏的环境,该环境可能定义变量、定义⾃定义函数、提供类型转换等等,从我们⾓度看是“在哪⼲”;
四、根对象及活动上下⽂对象: 根对象是默认的活动上下⽂对象,活动上下⽂对象表⽰了当前表达式操作的对象,从我们⾓度看是“对谁
⼲”。
理解了这些概念后,让我们看下SpEL如何⼯作的呢,如图所⽰:
⼯作原理
1.⾸先定义表达式:“1+2”;
2.定义解析器ExpressionParser实现,SpEL提供默认实现SpelExpressionParser;
2.1.SpelExpressionParser解析器内部使⽤Tokenizer类进⾏词法分析,即把字符串流分析为记号流,记号在SpEL使⽤Token类来表⽰;
2.2.有了记号流后,解析器便可根据记号流⽣成内部抽象语法树;在SpEL中语法树节点由SpelNode接⼝实现代表:如OpPlus表⽰加操作节点、IntLiteral表⽰int型字⾯
2.3.对外提供Expression接⼝来简化表⽰抽象语法树,从⽽隐藏内部实现细节,并提供getValue简单⽅法⽤于获取表达式值;SpEL提供默认实现为SpelExpression;
3.定义表达式上下⽂对象(可选),SpEL使⽤EvaluationContext接⼝表⽰上下⽂对象,⽤于设置根对象、⾃定义变量、⾃定义函数、类型转换器等,SpEL提供默认实
4.使⽤表达式对象根据上下⽂对象(可选)求值(调⽤表达式对象的getValue⽅法)获得结果。
接下来让我们看下SpEL的主要接⼝吧:
ExpressionParser接⼝
表⽰解析器,默认实现是pression.spel.standard包中的SpelExpressionParser类,使⽤parseExpression⽅法将字符串表达式转换为Expression对象,对于ParserContext接⼝⽤于定义字符串表达式是不是模板,及模板开始与结束字符:
public interface ExpressionParser {
Expression parseExpression(String expressionString) throws ParseException;
Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}
来看下⽰例:
@Test
public void testParserContext() {
ExpressionParser parser = new SpelExpressionParser();
ParserContext parserContext = new ParserContext() {
@Override
public boolean isTemplate() {
return true;
}
@Override
public String getExpressionPrefix() {
return "#{";
}
@Override
public String getExpressionSuffix() {
return "}";
}
};
String template = "#{'Hello '}#{'World!'}";
Expression expression = parser.parseExpression(template, parserContext);
System.out.Value());
}
在此我们演⽰的是使⽤ParserContext的情况,此处定义了ParserContext实现:定义表达式是模块,表达式前缀为“#{”,后缀
为“}”;使⽤parseExpression解析时传⼊的模板必须以“#{”开头,以“}”结尾,如"#{'Hello '}#{'World!'}"。
默认传⼊的字符串表达式不是模板形式,如之前演⽰的Hello World。
EvaluationContext接⼝
表⽰上下⽂环境,默认实现是pression.spel.support包中的StandardEvaluationContext类,使⽤setRootObject⽅法来设置根对象,使⽤setVariable⽅法来注册⾃定义变量,使⽤registerFunction来注册⾃定义函数等等。
Expression接⼝
表⽰表达式对象,默认实现是pression.spel.standard包中的SpelExpression,提供getValue⽅法⽤于获取表达式值,提供setValue⽅法⽤于设置对象值。
了解了SpEL原理及接⼝,接下来的事情就是SpEL语法了。
SpEL语法
基本表达式
字⾯量表达式
SpEL⽀持的字⾯量包括:字符串、数字类型(int、long、float、double)、布尔类型、null类型。
类型⽰例字符串String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);数字类型int int1 = parser.parseExpression("1").getValue(Integer.class);
long long1 = parser.parseExpression("-1L").getValue(long.class);
float float1 = parser.parseExpression("1.1").getValue(Float.class);
double double1 = parser.parseExpression("1.1E+2").getValue(double.class);
int hex1 = parser.parseExpression("0xa").getValue(Integer.class);
long hex2 = parser.parseExpression("0xaL").getValue(long.class);布尔类型boolean true1 =
parser.parseExpression("true").getValue(boolean.class);
boolean false1 = parser.parseExpression("false").getValue(boolean.class);null类型Object null1 =
parser.parseExpression("null").getValue(Object.class);
@Test
public void test2() {
ExpressionParser parser = new SpelExpressionParser();
String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);
int int1 = parser.parseExpression("1").getValue(Integer.class);
long long1 = parser.parseExpression("-1L").getValue(long.class);
float float1 = parser.parseExpression("1.1").getValue(Float.class);
double double1 = parser.parseExpression("1.1E+2").getValue(double.class);
int hex1 = parser.parseExpression("0xa").getValue(Integer.class);
long hex2 = parser.parseExpression("0xaL").getValue(long.class);
boolean true1 = parser.parseExpression("true").getValue(boolean.class);
boolean false1 = parser.parseExpression("false").getValue(boolean.class);
Object null1 = parser.parseExpression("null").getValue(Object.class);
System.out.println("str1=" + str1);
System.out.println("int1=" + int1);
System.out.println("long1=" + long1);
System.out.println("float1=" + float1);
System.out.println("double1=" + double1);
System.out.println("hex1=" + hex1);
System.out.println("hex2=" + hex2);
System.out.println("true1=" + true1);
System.out.println("false1=" + false1);
System.out.println("null1=" + null1);
}
输出
str1=Hello World!
int1=1
long1=-1
float1=1.1
double1=110.0
hex1=10
hex2=10
true1=true
false1=false
null1=null
算数运算表达式
SpEL⽀持加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)运算。
类型⽰例加减乘除int result1 = parser.parseExpression("1+2-3*4/2").getValue(Integer.class);//-3求余int result2 =
parser.parseExpression("4%3").getValue(Integer.class);//1幂运算int result3 =
parser.parseExpression("2^3").getValue(Integer.class);//8
SpEL还提供求余(MOD)和除(DIV)⽽外两个运算符,与“%”和“/”等价,不区分⼤⼩写。
关系表达式
等于(==)、不等于(!=)、⼤于(>)、⼤于等于(>=)、⼩于(<)、⼩于等于(<=),区间(between)运算。
如parser.parseExpression("1>2").getValue(boolean.class);将返回false;
⽽parser.parseExpression("1 between {1, 2}").getValue(boolean.class);将返回true。
between运算符右边操作数必须是列表类型,且只能包含2个元素。第⼀个元素为开始,第⼆个元素为结束,区间运算是包含边界值的,即xxx>=(0) && xxx<=(1)。
SpEL同样提供了等价的“EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”来表⽰等于、不等于、⼤于、⼤于等于、⼩于、⼩于等于,不区分⼤⼩写。
@Test
public void test3() {
ExpressionParser parser = new SpelExpressionParser();
boolean v1 = parser.parseExpression("1>2").getValue(boolean.class);
boolean between1 = parser.parseExpression("1 between {1,2}").getValue(boolean.class);
System.out.println("v1=" + v1);
System.out.println("between1=" + between1);
}
输出
v1=false
between1=true
逻辑表达式
且(and或者&&)、或(or或者||)、⾮(!或NOT)。
@Test
public void test4() {
ExpressionParser parser = new SpelExpressionParser();
boolean result1 = parser.parseExpression("2>1 and (!true or !false)").getValue(boolean.class);
boolean result2 = parser.parseExpression("2>1 && (!true || !false)").getValue(boolean.class);
boolean result3 = parser.parseExpression("2>1 and (NOT true or NOT false)").getValue(boolean.class);
boolean result4 = parser.parseExpression("2>1 && (NOT true || NOT false)").getValue(boolean.class);
System.out.println("result1=" + result1);
System.out.println("result2=" + result2);
System.out.println("result3=" + result3);
System.out.println("result4=" + result4);
}
输出

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。