RuoYi-Vue-Plus (SPEL 表达式)

 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类,

  1. 使用setRootObject方法来设置根对象,
  2. 使用setVariable方法来注册自定义变量,
  3. 使用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-表达式赋值 、表达式模版

如上面标题二,就是表达式赋值

如上面标题三,表达式模版

其他调用类方法,转换类型之类不常用,可以自行查看下面知乎文档链接 

总结

  1. Spel功能还是比较强大的,可以脱离spring环境独立运行
  2. spel可以用在一些动态规则的匹配方面,比如监控系统中监控规则的动态匹配;其他的一些条件动态判断等等
  3. 本文内容比较长,建议大家把案例都敲一遍,可以设置一些断点去研究一下源码,有问题的,欢迎大家留言交流。

 本文参考 :

玩转Spring中强大的spel表达式! - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/174786047

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值