likes
comments
collection
share

QLExpress 入门教程

作者站长头像
站长
· 阅读数 28

引言

QLExpress 是一款由阿里巴巴开发的针对Java平台的动态语言,旨在提升开发者在各类业务场景下的效率与灵活性。它源自阿里巴巴内部复杂的电商需求,包括业务规则管理、布尔表达式计算、高精度数学运算等,并于2012年对外开源。QLExpress以其轻量、高效、安全的特性,在阿里集团内外产生了广泛影响。 地址:github.com/alibaba/QLE…

依赖与安装

Maven依赖

在你的项目pom.xml文件中加入以下依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>QLExpress</artifactId>
    <version>3.3.3</version>
</dependency>

快速开始

创建 ExpressRunner 实例并执行简单脚本:

import com.alibaba.qlExpress.ExpressRunner;
import com.alibaba.qlExpress.DefaultContext;

public class QLExpressDemo {
    public static void main(String[] args) {
        ExpressRunner runner = new ExpressRunner();
        DefaultContext<String, Object> context = new DefaultContext<>();
        context.put("a", 1);
        context.put("b", 2);
        context.put("c", 3);
        String express = "a + b * c";
        try {
            Object r = runner.execute(express, context, null, true, false);
            System.out.println(r); // 输出结果:7
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

语法与特性

基础语法

QLExpress 支持大部分Java的运算符,如 +, -, *, /, 比较运算符,逻辑运算符,以及流程控制语句 for, if-then-else 等。

  • 变量定义:var x = 10;
  • 循环:for (var i = 0; i < 10; i++) { ... }
  • 条件语句:if (x > 10) { ... } else { ... }
  • 函数定义:function add(a, b) { return a + b; }

弱类型特性

QLExpress 是弱类型语言,类似于 Groovy 和 JavaScript,这使得编写业务逻辑更为灵活,但也意味着牺牲了部分编译时的类型安全检查。

安全控制

QLExpress提供了多级别安全控制,以防止潜在的安全风险,如防止死循环和不安全的系统API调用。你可以通过设置maxStack和maxCPUTime参数来限制递归深度和执行时间。

runner.setMaxStack(100);
runner.setMaxCPUTime(1000);

黑名单机制

默认禁用了一些危险的API调用,例如 System.exit,并提供了接口供用户扩展黑名单。

白名单机制

分为编译期白名单和运行时白名单,可以精确控制脚本中允许使用的类和方法。

语法树与静态分析

QLExpress 提供API来静态分析脚本,例如获取脚本中使用的变量和函数名称,这有助于构建上下文或进行系统业务处理。

优化与缓存

引擎使用本地缓存加速重复脚本的执行,同时提供了API来管理缓存,例如清除缓存或从缓存中获取指令集。

高级特性

  • 防止死循环:通过设置脚本执行超时时间来防止无限循环。
  • 函数与操作符扩展:允许自定义函数、操作符和宏,扩展QLExpress的能力。
  • 与Spring集成:通过自定义 IExpressContext 实现与Spring框架的无缝对接,便于在脚本中访问Spring Bean。

示例

自定义函数

runner.addFunction("max", new OperatorBase() {
    @Override
    public Object executeInner(Object[] list) throws Exception {
        double max = Double.NEGATIVE_INFINITY;
        for (Object obj : list) {
            max = Math.max(max, ((Number)obj).doubleValue());
        }
        return max;
    }
});

执行带自定义函数的脚本

context.put("numbers", new int[]{1, 2, 3, 4, 5});
String script = "max(numbers)";
Object result = runner.execute(script, context, null, true, false);
System.out.println(result); // 输出最大值:5

扩展操作符

QLExpress允许用户自定义操作符,从而扩展语言的功能。例如,你可以定义一个名为join的操作符,用于合并多个元素到一个列表中。以下是如何实现这个自定义操作符的步骤:

import java.util.ArrayList;
import java.util.List;

public class JoinOperator extends Operator {
    @Override
    public Object executeInner(Object[] list) throws Exception {
        List<Object> result = new ArrayList<>();
        for (Object obj : list) {
            if (obj instanceof List) {
                result.addAll((List<?>) obj);
            } else {
                result.add(obj);
            }
        }
        return result;
    }
}

在QLExpress运行环境中注册此操作符:

ExpressRunner runner = new ExpressRunner();
runner.addOperator("join", new JoinOperator());

// 使用自定义的join操作符
Object r = runner.execute("1 join 2 join 3", context, null, false, false);
System.out.println(r); // 输出: [1, 2, 3]

绑定Java类或对象的Method

QLExpress支持将Java类的方法直接绑定到脚本中使用,这包括静态方法和实例方法。这样做的好处是可以直接在脚本里调用Java代码,无需手动实现操作符。

绑定静态方法

runner.addFunctionOfClassMethod("toUpperCase", String.class.getName(), "toUpperCase", new String[]{"String"}, null);

在脚本中调用:

String express = "toUpperCase(\"hello\")";
Object result = runner.execute(express, context, null, false, false);
System.out.println(result); // 输出: HELLO

绑定实例方法

假设有一个BeanExample类,我们想在脚本中使用它的anyContains方法。

public class BeanExample {
    public boolean anyContains(String str, String searchStr) {
        // 方法实现略
    }
}

BeanExample bean = new BeanExample();
runner.addFunctionOfServiceMethod("containsChar", bean, "anyContains", new Class[]{String.class, String.class}, null);

String express = "containsChar('hello', 'e')";
Object r = runner.execute(express, context, null, false, false);
System.out.println(r); // 输出结果根据方法实现

Macro 宏定义

宏定义是QLExpress中一种简化重复表达式的机制,它允许你为一段常用表达式定义一个名字,之后在脚本中可以直接使用这个名字。这类似于编程语言中的函数,但更加简单,不涉及参数和返回值的概念。

runner.addMacro("calculateTotalPrice", "price * quantity + tax");

IExpressContext<String, Object> context = new DefaultContext<>();
context.put("price", 100);
context.put("quantity", 5);
context.put("tax", 5);

Object result = runner.execute("calculateTotalPrice", context, null, false, false);
System.out.println(result); // 输出: 505

在上述示例中,calculateTotalPrice 宏定义了一个计算总价格的公式,当在脚本中引用该宏时,QLExpress会自动替换为对应的表达式并计算结果。

结语

QLExpress凭借其灵活性和高性能,成为了处理复杂业务逻辑的理想选择。掌握QLExpress的使用,将极大地提升你在Java项目中的业务规则处理能力和效率。随着深入学习和实践,你会发现更多高级特性和技巧,助力你解决更复杂的问题。

转载自:https://juejin.cn/post/7385935890932482067
评论
请登录