RuoYi-Vue-Plus 中SPEL使用
DataScopeType 枚举类中:/*** 部门数据权限*/DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "),
PlusDataPermissionHandler 拦截器中定义了解析器:buildDataFilter 方法中根据注解的key value来进行SPEL解析:key作为占位符,value 设置进原生sql
@Slf4j public class PlusDataPermissionHandler { /*** spel 解析器*/private final ExpressionParser parser = new SpelExpressionParser();private final ParserContext parserContext = new TemplateParserContext();/*** 构造数据过滤sql*/private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) {。。。。。省略代码for (DataColumn dataColumn : dataColumns) {if (dataColumn.key().length != dataColumn.value().length) {throw new ServiceException("角色数据范围异常 => key与value长度不匹配");}if (!StringUtils.containsAny(type.getSqlTemplate(),Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new))) {continue;}//1- 设置注解变量 key 为表达式变量 value 为变量值for (int i = 0; i < dataColumn.key().length; i++) {context.setVariable(dataColumn.key()[i], dataColumn.value()[i]);}//2- 解析sql模板并填充String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class);conditions.add(joinStr + sql);isSuccess = true;}。。。。。省略代码}
一、概念
表达式语言给静态Java语言增加了动态功能。
SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用。
SpEL支持如下表达式:
一、基本表达式: 字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式;
二、类相关表达式: 类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用;
三、集合相关表达式: 内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义;
四、其他表达式:模板表达式。
二、DEMO 测试:
@Testpublic void test1() {//1-首先构造一个解析器ExpressionParser parser = new SpelExpressionParser();//2-其次解析器解析字符串表达式Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");//3- 创建上下文EvaluationContext context = new StandardEvaluationContext();//4-替换context.setVariable("end", "!");//5-求值:通过Expression接口的getValue方法,传入上下文获得表达式值。System.out.println(expression.getValue(context));}
1- parser.parseExpression("('Hello' + ' World').concat(#end)");
上面表达式拼接了 #end
2- context.setVariable("end", "!");
上下文中#end 替换为 !
输出:
Hello World!
三、ExpressionParser接口
public interface ExpressionParser {Expression parseExpression(String expressionString) throws ParseException;Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}
Expression parseExpression(String expressionString, ParserContext context)
传入 ParserContext 的实现类 TemplateParserContext 可以自定义解析前缀。
public interface ParserContext {//是否是模板boolean isTemplate();//模板表达式前缀String getExpressionPrefix();//模板表达式后缀String getExpressionSuffix(); }
对上面DEMO案列更改:
@Testpublic void test1() {ExpressionParser parser = new SpelExpressionParser();// ${ 定义解析前缀Expression expression = parser.parseExpression("HelloWorld${#end}", new TemplateParserContext("${","}") );EvaluationContext context = new StandardEvaluationContext();context.setVariable("end", "!");。System.out.println(expression.getValue(context));}
表达式模版例子:
TemplateParserContext 解析器使用
@Test
public void test11() {//创建解析器SpelExpressionParser parser = new SpelExpressionParser();//创建解析器上下文ParserContext context = new TemplateParserContext("%{", "}");Expression expression = parser.parseExpression("你好:%{#name},我们正在学习:%{#lesson}", context);//创建表达式计算上下文EvaluationContext evaluationContext = new StandardEvaluationContext();evaluationContext.setVariable("name", "路人甲java");evaluationContext.setVariable("lesson", "spring高手系列!");//获取值String value = expression.getValue(evaluationContext, String.class);System.out.println(value);
}
四、EvaluationContext接口
表示上下文环境,默认实现是org.springframework.expression.spel.support包中的StandardEvaluationContext类,
- 使用setRootObject方法来设置根对象,
- 使用setVariable方法来注册自定义变量,
- 使用registerFunction来注册自定义函数等等。
五、 SPEL 语法
1-基础字面量
SpEL支持的字面量包括:字符串、数字类型(int、long、float、double)、布尔类型、null类型。
@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
2-算数表达式:
SpEL支持加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)运算。
SpEL还提供求余(MOD)和除(DIV)而外两个运算符,与“%”和“/”等价,不区分大小写。
//类型示例加减乘除int result1 = parser.parseExpression("1+2-3*4/2").getValue(Integer.class);//求余
int result2 = parser.parseExpression("4%3").getValue(Integer.class);//幂运算
int result3 = parser.parseExpression("2^3").getValue(Integer.class);
输出:
-3 1 8
3-关系表达式
等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)、小于等于(<=),区间(between)运算。
@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
4-逻辑表达式
且(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);
}
输出:
result1=true result2=true result3=true result4=false
5-正则表达式
使用“str matches regex
如“”将返回true;ExpressionParser parser = new SpelExpressionParser();boolean result1 = parser.parseExpression("'123' matches '\d{3}'").getValue(boolean.class);
输出:
true
6-表达式赋值 、表达式模版
如上面标题二,就是表达式赋值
如上面标题三,表达式模版
其他调用类方法,转换类型之类不常用,可以自行查看下面知乎文档链接
总结
- Spel功能还是比较强大的,可以脱离spring环境独立运行
- spel可以用在一些动态规则的匹配方面,比如监控系统中监控规则的动态匹配;其他的一些条件动态判断等等
- 本文内容比较长,建议大家把案例都敲一遍,可以设置一些断点去研究一下源码,有问题的,欢迎大家留言交流。
本文参考 :
玩转Spring中强大的spel表达式! - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/174786047