• 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成为一个在阿里电商业务场景中得到广泛应用的强大工具,具有高效、灵活和安全的特点。

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

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

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

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

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

    1. com.ql
    2. qlExpress
    3. 3.2.2

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

    1. package org.zyf.javabasic.qlexpress;
    2. import com.ql.util.express.DefaultContext;
    3. import com.ql.util.express.ExpressRunner;
    4. /**
    5. * @program: zyfboot-javabasic
    6. * @description: 演示如何使用 QLExpress 计算折扣后的金额
    7. * @author: zhangyanfeng
    8. * @create: 2023-11-12 21:40
    9. **/
    10. public class QLExpressExample {
    11. public static void main(String[] args) {
    12. try {
    13. // 创建 QLExpress 引擎
    14. ExpressRunner runner = new ExpressRunner();
    15. // 创建上下文并设置变量
    16. DefaultContext context = new DefaultContext<>();
    17. context.put("amount", 1000);
    18. context.put("discount", 0.1);
    19. // 执行脚本
    20. String expression = "amount * (1 - discount)";
    21. Object result = runner.execute(expression, context, null, true, false);
    22. // 输出结果
    23. System.out.println("Result: " + result);
    24. } catch (Exception e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. }

    二、基本语法学习

    (一)操作符

    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 || condition2逻辑或,两个条件中有一个为真时结果为真
    !!condition逻辑非,将真变为假,假变为真
    三元运算符? :condition ? valueIfTrue : valueIfFalse用于根据条件选择两个值中的一个

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

    1. package org.zyf.javabasic.qlexpress;
    2. import com.ql.util.express.DefaultContext;
    3. import com.ql.util.express.ExpressRunner;
    4. /**
    5. * @program: zyfboot-javabasic
    6. * @description: 一些简单的运算和条件判断
    7. * @author: zhangyanfeng
    8. * @create: 2023-11-12 21:48
    9. **/
    10. public class Calculator {
    11. public static void main(String[] args) {
    12. try {
    13. ExpressRunner runner = new ExpressRunner();
    14. DefaultContext context = new DefaultContext<>();
    15. // 设置变量
    16. context.put("a", 10);
    17. context.put("b", 5);
    18. // 算术运算示例
    19. executeAndPrint(runner, context, "a + b", "Addition");
    20. // 比较运算示例
    21. executeAndPrint(runner, context, "a > b", "Greater than");
    22. // 逻辑运算示例
    23. executeAndPrint(runner, context, "a > 0 && b > 0", "Logical AND");
    24. // 三元运算示例
    25. executeAndPrint(runner, context, "a > b ? 'a is greater' : 'b is greater'", "Ternary Operator");
    26. } catch (Exception e) {
    27. e.printStackTrace();
    28. }
    29. }
    30. private static void executeAndPrint(ExpressRunner runner, DefaultContext context, String expression, String operation) throws Exception {
    31. // 执行脚本
    32. Object result = runner.execute(expression, context, null, true, false);
    33. // 输出结果
    34. System.out.println(operation + ": " + result);
    35. }
    36. }

    (二)Java对象操作

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

    1. package org.zyf.javabasic.qlexpress;
    2. import com.ql.util.express.DefaultContext;
    3. import com.ql.util.express.ExpressRunner;
    4. /**
    5. * @program: zyfboot-javabasic
    6. * @description: 基本的 Java 语法和对象操作
    7. * @author: zhangyanfeng
    8. * @create: 2023-11-12 21:54
    9. **/
    10. public class QLExpressJavaSyntaxExample {
    11. public static void main(String[] args) {
    12. try {
    13. ExpressRunner runner = new ExpressRunner();
    14. DefaultContext context = new DefaultContext<>();
    15. // 创建一个用户对象
    16. User user = new User("John", 25);
    17. context.put("user", user);
    18. // 使用 Java 语法访问对象属性和调用方法
    19. executeAndPrint(runner, context, "user.getName()", "Accessing Object Property");
    20. executeAndPrint(runner, context, "user.getAge() + 5", "Performing Arithmetic with Object Property");
    21. // 使用 Java 语法进行对象操作
    22. executeAndPrint(runner, context, "user.age = 30", "Modifying Object Property");
    23. } catch (Exception e) {
    24. e.printStackTrace();
    25. }
    26. }
    27. private static void executeAndPrint(ExpressRunner runner, DefaultContext context, String expression, String operation) throws Exception {
    28. // 执行脚本
    29. Object result = runner.execute(expression, context, null, true, false);
    30. // 输出结果
    31. System.out.println(operation + ": " + result);
    32. }
    33. // 用户对象类
    34. static class User {
    35. private String name;
    36. private int age;
    37. public User(String name, int age) {
    38. this.name = name;
    39. this.age = age;
    40. }
    41. public String getName() {
    42. return name;
    43. }
    44. public int getAge() {
    45. return age;
    46. }
    47. }
    48. }

    (三) 脚本中定义function

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

    1. package org.zyf.javabasic.qlexpress;
    2. import com.ql.util.express.DefaultContext;
    3. import com.ql.util.express.ExpressRunner;
    4. /**
    5. * @program: zyfboot-javabasic
    6. * @description: 通过 function 关键字来定义函数
    7. * @author: zhangyanfeng
    8. * @create: 2023-11-12 22:11
    9. **/
    10. public class QLExpressFunctionExample {
    11. public static void main(String[] args) {
    12. try {
    13. final String express = "function add(int a, int b){\n" +
    14. " return a + b;\n" +
    15. "};\n" +
    16. "\n" +
    17. "function sub(int a, int b){\n" +
    18. " return a - b;\n" +
    19. "};\n" +
    20. "\n" +
    21. "a = 10;\n" +
    22. "result = add(a, 4) + sub(a, 9);\n" +
    23. "return result;";
    24. ExpressRunner runner = new ExpressRunner();
    25. DefaultContext context = new DefaultContext<>();
    26. // 执行脚本
    27. Object result = runner.execute(express, context, null, true, false);
    28. // 输出脚本执行结果
    29. System.out.println("Result: " + result);
    30. // 输出函数调用过程中的参数和返回值
    31. System.out.println("add function call: a + 4 = " + context.get("a") + " + 4 = " + context.get("result"));
    32. } catch (Exception e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. }

    (四)扩展操作符

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

    1. package org.zyf.javabasic.qlexpress;
    2. import com.ql.util.express.DefaultContext;
    3. import com.ql.util.express.ExpressRunner;
    4. import java.util.ArrayList;
    5. import java.util.List;
    6. /**
    7. * @program: zyfboot-javabasic
    8. * @description: 自定义操作符(Operator)来扩展语言的功能
    9. * @author: zhangyanfeng
    10. * @create: 2023-11-12 23:13
    11. **/
    12. public class QLExpressOperatorExample {
    13. public static void main(String[] args) {
    14. try {
    15. // 示例 1:替换 if then else 关键字
    16. ExpressRunner runner1 = new ExpressRunner();
    17. DefaultContext context1 = new DefaultContext<>();
    18. context1.put("语文",120);
    19. context1.put("数学",23);
    20. context1.put("英语",23);
    21. runner1.addOperatorWithAlias("如果", "if", null);
    22. runner1.addOperatorWithAlias("则", "then", null);
    23. runner1.addOperatorWithAlias("否则", "else", null);
    24. String express1 = "如果 (语文 + 数学 + 英语 > 270) 则 {return 1;} 否则 {return 0;}";
    25. Object result1 = runner1.execute(express1, context1, null, false, false, 100L);
    26. System.out.println("Result 1: " + result1); // 输出结果 1
    27. // 示例 2:自定义 Operator
    28. ExpressRunner runner2 = new ExpressRunner();
    29. DefaultContext context2 = new DefaultContext<>();
    30. // 自定义 Operator
    31. runner2.addOperator("join", new JoinOperator());
    32. // 示例 2.1:addOperator
    33. Object result2_1 = runner2.execute("1 join 2 join 3", context2, null, false, false);
    34. System.out.println("Result 2.1: " + result2_1); // 输出结果 [1, 2, 3]
    35. // 示例 2.2:replaceOperator
    36. ExpressRunner runner2_2 = new ExpressRunner();
    37. runner2_2.replaceOperator("+", new JoinOperator());
    38. Object result2_2 = runner2_2.execute("1 + 2 + 3", context2, null, false, false);
    39. System.out.println("Result 2.2: " + result2_2); // 输出结果 [1, 2, 3]
    40. // 示例 2.3:addFunction
    41. ExpressRunner runner2_3 = new ExpressRunner();
    42. runner2_3.addFunction("join", new JoinOperator());
    43. Object result2_3 = runner2_3.execute("join(1, 2, 3)", context2, null, false, false);
    44. System.out.println("Result 2.3: " + result2_3); // 输出结果 [1, 2, 3]
    45. } catch (Exception e) {
    46. e.printStackTrace();
    47. }
    48. }
    49. // JoinOperator 类的定义
    50. public static class JoinOperator extends com.ql.util.express.Operator {
    51. public Object executeInner(Object[] list) throws Exception {
    52. Object opdata1 = list[0];
    53. Object opdata2 = list[1];
    54. if (opdata1 instanceof List) {
    55. ((List) opdata1).add(opdata2);
    56. return opdata1;
    57. } else {
    58. List result = new ArrayList();
    59. for (Object opdata : list) {
    60. result.add(opdata);
    61. }
    62. return result;
    63. }
    64. }
    65. }
    66. }

    (五)扩展操作符

  • 相关阅读:
    ESP8266/ESP32 +1.3“ or 0.96“ IIC OLED指针式时钟
    docker stop slow 解决
    Qt-OpenCV学习笔记--基础知识和基本操作--总结
    国庆期间“头像+国旗”玩法是如何实现的?
    AI量化与机器学习流程:从数据到模型
    WSL 2 上启用微软官方支持的 systemd
    2021Java面试题库大全(内部资源)
    软件测试面试技巧有哪些?可以从这2个方面去进行准备
    处理Java异常的10个最佳实践
    深度解读低门槛钱包赛道:Web3的用户入口
  • 原文地址:https://blog.csdn.net/xiaofeng10330111/article/details/134363363