官网学习地址:https://github.com/alibaba/QLExpress
一、基本介绍
(一)快速了解
QLExpress(Quick Language Express)是阿里巴巴开源的一门动态脚本引擎解析工具,起源于阿里巴巴的电商业务,旨在解决业务规则、表达式、数学计算等动态脚本的解析问题。其具有以下基本特点:
-
线程安全 :QLExpress被设计为线程安全的动态脚本引擎,它使用
threadlocal
类型的临时变量,确保在引擎运算过程中的并发场景下的线程安全性。 -
高效执行:为了提高执行效率,QLExpress在编译过程中可以将比较耗时的脚本编译结果缓存到本地机器。此外,运行时的临时变量创建采用了缓冲池技术,以确保高效的运行时性能,使其与一些性能优秀的脚本引擎(如Groovy)相当。
-
弱类型脚本语言:QLExpress采用弱类型脚本语言,语法类似于Groovy和JavaScript。这使得业务规则的表达更加灵活,虽然相对于强类型脚本语言可能略慢,但在业务的灵活性方面提供了很大的优势。
-
安全控制:QLExpress提供了一些运行时参数的设置,以进行安全控制。通过这些参数,可以预防一些潜在的安全问题,如死循环或对高危系统API的调用。
-
代码精简、依赖最小: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;
}
}
}
}
(五)扩展操作符