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 测试:
@Test
public 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案列更改:
@Test
public 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