QLExpress学习使用总结

官网学习地址:https://github.com/alibaba/QLExpress

一、基本介绍

(一)快速了解

QLExpress(Quick Language Express)是阿里巴巴开源的一门动态脚本引擎解析工具,起源于阿里巴巴的电商业务,旨在解决业务规则、表达式、数学计算等动态脚本的解析问题。其具有以下基本特点:

  1. 线程安全 :QLExpress被设计为线程安全的动态脚本引擎,它使用threadlocal类型的临时变量,确保在引擎运算过程中的并发场景下的线程安全性。

  2. 高效执行:为了提高执行效率,QLExpress在编译过程中可以将比较耗时的脚本编译结果缓存到本地机器。此外,运行时的临时变量创建采用了缓冲池技术,以确保高效的运行时性能,使其与一些性能优秀的脚本引擎(如Groovy)相当。

  3. 弱类型脚本语言:QLExpress采用弱类型脚本语言,语法类似于Groovy和JavaScript。这使得业务规则的表达更加灵活,虽然相对于强类型脚本语言可能略慢,但在业务的灵活性方面提供了很大的优势。

  4. 安全控制:QLExpress提供了一些运行时参数的设置,以进行安全控制。通过这些参数,可以预防一些潜在的安全问题,如死循环或对高危系统API的调用。

  5. 代码精简、依赖最小:QLExpress的设计追求代码的精简和最小依赖,其jar包大小为250k,适用于所有Java的运行环境。这使得它在各种环境中都能轻松部署和运行,包括在Android系统的低端POS机上广泛应用。

总体而言,这些特性使QLExpress成为一个在阿里电商业务场景中得到广泛应用的强大工具,具有高效、灵活和安全的特点。

(二)与常用规则引擎对比

特性 / 规则引擎 Drools Aviator EasyRule QLExpress
语言 Drools规则语言 (DRL) Aviator表达式语言 Java 弱类型脚本语言
性能 适用于复杂规则,可能较慢 高性能表达式求值引擎 相对较高性能,适用于简单规则 高效执行,适用于业务规则和表达式计算
灵活性 非常灵活,支持动态修改规则 灵活,支持丰富的运算符和函数 简单易用,适合非专业开发人员 灵活,支持业务规则、表达式和数学计算
语法 专门的规则语言 表达式语言 Java编写规则 弱类型脚本语言,类似于Groovy和JavaScript
应用场景 复杂的业务规则 简单的表达式计算和规则 简单规则场景,面向非专业开发人员 业务规则、表达式、数学计算,适用于电商业务
开发者社区 大型开发者社区 相对较小的社区规模 相对较小的社区规模 相对较小的社区规模,阿里巴巴内部有影响力
文档 详尽的文档 文档相对较少 文档相对较少 文档相对较少,可能需要深入源代码理解
开源

Drools适用于复杂的业务规则,而Aviator和QLExpress适用于相对简单的表达式计算和规则。EasyRule更适合简单规则场景,特别是面向非专业开发人员的情况。最终选择取决于具体需求,包括业务规则的复杂性、性能要求、开发人员技能水平以及项目的特定场景。

(三)引用说明与基本展示

在 Maven 项目中引入 QLExpress,需要在项目的 pom.xml 文件中添加相关的依赖:

java 复制代码
<dependencies>
    <dependency>
        <groupId>com.ql</groupId>
        <artifactId>qlExpress</artifactId>
        <version>3.2.2</version> <!-- 使用实际版本号 -->
    </dependency>
</dependencies>

以下展示简单演示如何使用 QLExpress 计算折扣后的金额。在实际项目中,可能需要更复杂的脚本和上下文,以适应业务需求。

java 复制代码
package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
 * @program: zyfboot-javabasic
 * @description: 演示如何使用 QLExpress 计算折扣后的金额
 * @author: zhangyanfeng
 * @create: 2023-11-12 21:40
 **/
public class QLExpressExample {
    public static void main(String[] args) {
        try {
            // 创建 QLExpress 引擎
            ExpressRunner runner = new ExpressRunner();

            // 创建上下文并设置变量
            DefaultContext<String, Object> context = new DefaultContext<>();
            context.put("amount", 1000);
            context.put("discount", 0.1);

            // 执行脚本
            String expression = "amount * (1 - discount)";
            Object result = runner.execute(expression, context, null, true, false);

            // 输出结果
            System.out.println("Result: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

二、基本语法学习

(一)操作符

QLExpress 支持一系列操作符,包括算术运算符、比较运算符、逻辑运算符等。

类别 操作符 示例 描述
算术运算符 + a + b 加法,将两个数字相加
算术运算符 - a - b 减法,从第一个数字中减去第二个数字
算术运算符 * a * b 乘法,将两个数字相乘
算术运算符 / a / b 除法,将第一个数字除以第二个数字
算术运算符 % a % b 取余,返回第一个数字除以第二个数字的余数
比较运算符 == a == b 等于,判断两个值是否相等
比较运算符 != a != b 不等于,判断两个值是否不相等
比较运算符 > a > b 大于,判断第一个值是否大于第二个值
比较运算符 < a < b 小于,判断第一个值是否小于第二个值
比较运算符 >= a >= b 大于等于,判断第一个值是否大于或等于第二个值
比较运算符 <= a <= b 小于等于,判断第一个值是否小于或等于第二个值
逻辑运算符 && condition1 && condition2 逻辑与,两个条件都为真时结果为真
逻辑运算符 || `condition1
逻辑运算符 ! !condition 逻辑非,将真变为假,假变为真
三元运算符 ? : condition ? valueIfTrue : valueIfFalse 用于根据条件选择两个值中的一个

定义一个 Calculator 类,其中包含一些数字和一个用户对象,然后使用 QLExpress 进行一些简单的运算和条件判断。

java 复制代码
package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
 * @program: zyfboot-javabasic
 * @description: 一些简单的运算和条件判断
 * @author: zhangyanfeng
 * @create: 2023-11-12 21:48
 **/
public class Calculator {
    public static void main(String[] args) {
        try {
            ExpressRunner runner = new ExpressRunner();
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 设置变量
            context.put("a", 10);
            context.put("b", 5);

            // 算术运算示例
            executeAndPrint(runner, context, "a + b", "Addition");

            // 比较运算示例
            executeAndPrint(runner, context, "a > b", "Greater than");

            // 逻辑运算示例
            executeAndPrint(runner, context, "a > 0 && b > 0", "Logical AND");

            // 三元运算示例
            executeAndPrint(runner, context, "a > b ? 'a is greater' : 'b is greater'", "Ternary Operator");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void executeAndPrint(ExpressRunner runner, DefaultContext<String, Object> context, String expression, String operation) throws Exception {
        // 执行脚本
        Object result = runner.execute(expression, context, null, true, false);

        // 输出结果
        System.out.println(operation + ": " + result);
    }
}

(二)Java对象操作

基本的 Java 语法和对象操作在 QLExpress 中同样适用,演示在 QLExpress 中使用 Java 语法和进行对象操作:

java 复制代码
package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
 * @program: zyfboot-javabasic
 * @description: 基本的 Java 语法和对象操作
 * @author: zhangyanfeng
 * @create: 2023-11-12 21:54
 **/
public class QLExpressJavaSyntaxExample {
    public static void main(String[] args) {
        try {
            ExpressRunner runner = new ExpressRunner();
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 创建一个用户对象
            User user = new User("John", 25);
            context.put("user", user);

            // 使用 Java 语法访问对象属性和调用方法
            executeAndPrint(runner, context, "user.getName()", "Accessing Object Property");
            executeAndPrint(runner, context, "user.getAge() + 5", "Performing Arithmetic with Object Property");

            // 使用 Java 语法进行对象操作
            executeAndPrint(runner, context, "user.age = 30", "Modifying Object Property");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void executeAndPrint(ExpressRunner runner, DefaultContext<String, Object> context, String expression, String operation) throws Exception {
        // 执行脚本
        Object result = runner.execute(expression, context, null, true, false);

        // 输出结果
        System.out.println(operation + ": " + result);
    }

    // 用户对象类
    static class User {
        private String name;
        private int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }
    }
}

(三) 脚本中定义function

在QLExpress中,可以通过 function 关键字来定义函数。以下是一个简单的示例:

java 复制代码
package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
 * @program: zyfboot-javabasic
 * @description: 通过 function 关键字来定义函数
 * @author: zhangyanfeng
 * @create: 2023-11-12 22:11
 **/
public class QLExpressFunctionExample {
    public static void main(String[] args) {
        try {
            final String express = "function add(int a, int b){\n" +
                    "  return a + b;\n" +
                    "};\n" +
                    "\n" +
                    "function sub(int a, int b){\n" +
                    "  return a - b;\n" +
                    "};\n" +
                    "\n" +
                    "a = 10;\n" +
                    "result = add(a, 4) + sub(a, 9);\n" +
                    "return result;";
            ExpressRunner runner = new ExpressRunner();
            DefaultContext<String, Object> context = new DefaultContext<>();

            // 执行脚本
            Object result = runner.execute(express, context, null, true, false);

            // 输出脚本执行结果
            System.out.println("Result: " + result);

            // 输出函数调用过程中的参数和返回值
            System.out.println("add function call: a + 4 = " + context.get("a") + " + 4 = " + context.get("result"));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(四)扩展操作符

在 QLExpress 中,可以通过自定义操作符(Operator)来扩展语言的功能。

java 复制代码
package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

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

/**
 * @program: zyfboot-javabasic
 * @description: 自定义操作符(Operator)来扩展语言的功能
 * @author: zhangyanfeng
 * @create: 2023-11-12 23:13
 **/
public class QLExpressOperatorExample {
    public static void main(String[] args) {
        try {
            // 示例 1:替换 if then else 关键字
            ExpressRunner runner1 = new ExpressRunner();
            DefaultContext<String, Object> context1 = new DefaultContext<>();
            context1.put("语文",120);
            context1.put("数学",23);
            context1.put("英语",23);

            runner1.addOperatorWithAlias("如果", "if", null);
            runner1.addOperatorWithAlias("则", "then", null);
            runner1.addOperatorWithAlias("否则", "else", null);

            String express1 = "如果 (语文 + 数学 + 英语 > 270) 则 {return 1;} 否则 {return 0;}";
            Object result1 = runner1.execute(express1, context1, null, false, false, 100L);
            System.out.println("Result 1: " + result1); // 输出结果 1

            // 示例 2:自定义 Operator
            ExpressRunner runner2 = new ExpressRunner();
            DefaultContext<String, Object> context2 = new DefaultContext<>();

            // 自定义 Operator
            runner2.addOperator("join", new JoinOperator());

            // 示例 2.1:addOperator
            Object result2_1 = runner2.execute("1 join 2 join 3", context2, null, false, false);
            System.out.println("Result 2.1: " + result2_1); // 输出结果 [1, 2, 3]

            // 示例 2.2:replaceOperator
            ExpressRunner runner2_2 = new ExpressRunner();
            runner2_2.replaceOperator("+", new JoinOperator());
            Object result2_2 = runner2_2.execute("1 + 2 + 3", context2, null, false, false);
            System.out.println("Result 2.2: " + result2_2); // 输出结果 [1, 2, 3]

            // 示例 2.3:addFunction
            ExpressRunner runner2_3 = new ExpressRunner();
            runner2_3.addFunction("join", new JoinOperator());
            Object result2_3 = runner2_3.execute("join(1, 2, 3)", context2, null, false, false);
            System.out.println("Result 2.3: " + result2_3); // 输出结果 [1, 2, 3]

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // JoinOperator 类的定义
    public static class JoinOperator extends com.ql.util.express.Operator {
        public Object executeInner(Object[] list) throws Exception {
            Object opdata1 = list[0];
            Object opdata2 = list[1];
            if (opdata1 instanceof List) {
                ((List) opdata1).add(opdata2);
                return opdata1;
            } else {
                List result = new ArrayList();
                for (Object opdata : list) {
                    result.add(opdata);
                }
                return result;
            }
        }
    }
}

(五)扩展操作符

相关推荐
Young_202202021 分钟前
学习笔记——KMP
笔记·学习
行然梦实17 分钟前
学习日记_20241110_聚类方法(K-Means)
学习·kmeans·聚类
马船长23 分钟前
制作图片木马
学习
秀儿还能再秀34 分钟前
机器学习——简单线性回归、逻辑回归
笔记·python·学习·机器学习
WCF向光而行39 分钟前
Getting accurate time estimates from your tea(从您的团队获得准确的时间估计)
笔记·学习
wang09071 小时前
工作和学习遇到的技术问题
学习
Li_0304062 小时前
Java第十四天(实训学习整理资料(十三)Java网络编程)
java·网络·笔记·学习·计算机网络
心怀梦想的咸鱼2 小时前
ue5 蓝图学习(一)结构体的使用
学习·ue5
kali-Myon3 小时前
ctfshow-web入门-SSTI(web361-web368)上
前端·python·学习·安全·web安全·web
龙中舞王3 小时前
Unity学习笔记(4):人物和基本组件
笔记·学习·unity