告別硬編碼:阿里開源的動態腳本引擎 QLExpress ,真香!
現代業務系統中,如何實現快速、靈活的規則配置和動態決策,成為了企業提升響應速度和智能化水平的關鍵。阿里巴巴開源的 QLExpress 引擎,以其輕量、高效、簡潔的優勢,為復雜業務邏輯的動態處理提供了一種創新的解決方案。
無論是需要實時調整規則的電商促銷,還是依賴規則動態性的金融風控,QLExpress 都能以其靈活的表達式和易用的規則配置,實現高效而精準的業務決策。
如果你正在尋找一款強大、靈活的動態腳本引擎,那么 QLExpress 可能正是你需要的工具!
一、QLExpress快速了解
QLExpress(Quick Language Express)是阿里巴巴開源的一門動態腳本引擎解析工具,起源于阿里巴巴的電商業務,旨在解決業務規則、表達式、數學計算等動態腳本的解析問題。其具有以下基本特點:
- 線程安全: QLExpress被設計為線程安全的動態腳本引擎,它使用threadlocal類型的臨時變量,確保在引擎運算過程中的并發場景下的線程安全性。
- 高效執行: 為了提高執行效率,QLExpress在編譯過程中可以將比較耗時的腳本編譯結果緩存到本地機器。此外,運行時的臨時變量創建采用了緩沖池技術,以確保高效的運行時性能,使其與一些性能優秀的腳本引擎(如Groovy)相當。
- 弱類型腳本語言: QLExpress采用弱類型腳本語言,語法類似于Groovy和JavaScript。這使得業務規則的表達更加靈活,雖然相對于強類型腳本語言可能略慢,但在業務的靈活性方面提供了很大的優勢。
- 安全控制: QLExpress提供了一些運行時參數的設置,以進行安全控制。通過這些參數,可以預防一些潛在的安全問題,如死循環或對高危系統API的調用。
- 代碼精簡、依賴最小: QLExpress的設計追求代碼的精簡和最小依賴,其jar包大小為250k,適用于所有Java的運行環境。這使得它在各種環境中都能輕松部署和運行,包括在Android系統的低端POS機上廣泛應用。
總體而言,這些特性使QLExpress成為一個在阿里電商業務場景中得到廣泛應用的強大工具,具有高效、靈活和安全的特點。
二、QLExpress與常用規則引擎對比
圖片
Drools適用于復雜的業務規則,而Aviator和QLExpress適用于相對簡單的表達式計算和規則。EasyRule更適合簡單規則場景,特別是面向非專業開發人員的情況。最終選擇取決于具體需求,包括業務規則的復雜性、性能要求、開發人員技能水平以及項目的特定場景。
三、快速引用和一般工作原理簡訴
1.引用與基本演示
在 Maven 項目中引入 QLExpress,需要在項目的 pom.xml 文件中添加相關的依賴:
<dependencies>
<dependency>
<groupId>com.ql</groupId>
<artifactId>qlExpress</artifactId>
<version>3.2.2</version> <!-- 使用實際版本號 -->
</dependency>
</dependencies>
以下展示簡單演示如何使用 QLExpress 計算折扣后的金額。在實際項目中,可能需要更復雜的腳本和上下文,以適應業務需求。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: 演示如何使用 QLExpress 計算折扣后的金額
* @author: zhangyanfeng
**/
publicclass 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();
}
}
}
2.一般工作原理說明
QLExpress的一般工作原理,包括語法樹分析、上下文和執行過程。
圖片
- 語法樹分析: QLExpress會先將輸入的腳本進行詞法分析和語法分析,生成一棵語法樹。這個語法樹表示了腳本的結構,將其組織成可以被執行的形式。
- 上下文: 在QLExpress中,上下文是一個關鍵概念。它是腳本執行時的環境,包含變量、函數等信息。在執行腳本之前,你可以向上下文中添加變量,定義函數,設置一些執行參數等。這樣,腳本執行時可以引用上下文中的內容。
- 執行過程: QLExpress的執行過程包括編譯和運行兩個主要階段。在編譯階段,腳本被解析并生成可以執行的指令序列。在運行階段,這些指令被執行,從而實現腳本的功能。
在這個框架下,二次定制的功能擴展可以包括:
- 自定義操作符和函數: 可以通過實現自定義的操作符和函數,使得腳本能夠執行特定的業務邏輯。
- 修改執行流程: 可以在執行階段插入自定義的邏輯,改變腳本執行的流程。
- 定制編譯過程: 在編譯階段定制特定的優化或變換,以滿足特殊需求。
- 擴展上下文功能: 可以添加一些上下文的攔截器,使得在腳本執行前后可以執行額外的邏輯。
這樣的擴展點允許更好地適應特定的業務場景和需求。
四、基本語法學習
1.操作符
QLExpress 支持一系列操作符,包括算術運算符、比較運算符、邏輯運算符等。
圖片
定義一個 Calculator 類,其中包含一些數字和一個用戶對象,然后使用 QLExpress 進行一些簡單的運算和條件判斷。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: 一些簡單的運算和條件判斷
* @author: zhangyanfeng
**/
publicclass 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);
}
}
2.Java對象操作
基本的 Java 語法和對象操作在 QLExpress 中同樣適用,演示在 QLExpress 中使用 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
**/
publicclass 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);
}
// 用戶對象類
staticclass User {
private String name;
privateint age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
3.腳本中定義function
在QLExpress中,可以通過 function 關鍵字來定義函數。以下是一個簡單的示例:
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: 通過 function 關鍵字來定義函數
* @author: zhangyanfeng
**/
publicclass 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();
}
}
}
4.擴展操作符
在 QLExpress 中,可以通過自定義操作符(Operator)來擴展語言的功能。
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
**/
publicclass 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 類的定義
publicstaticclass 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;
}
}
}
}
5.綁定Java類或對象的methon
在 QLExpress 中,可使用 addFunctionOfClassMethod 和 addFunctionOfServiceMethod 方法來綁定 Java 類或對象的方法。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: 可以使用 addFunctionOfClassMethod 和 addFunctionOfServiceMethod 方法來綁定 Java 類或對象的方法
* @author: zhangyanfeng
* @create: 2023-11-19 16:13
**/
publicclass QLExpressFunctionBindingExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 綁定 Math 類的 abs 方法
runner.addFunctionOfClassMethod("取絕對值", Math.class.getName(), "abs", new String[]{"double"}, null);
// 綁定 BeanExample 類的 upper 方法
runner.addFunctionOfClassMethod("轉換為大寫", BeanExample.class.getName(), "upper", new String[]{"String"}, null);
// 綁定 System.out 的 println 方法
runner.addFunctionOfServiceMethod("打印", System.out, "println", new String[]{"String"}, null);
// 綁定 BeanExample 對象的 anyContains 方法
runner.addFunctionOfServiceMethod("contains", new BeanExample(), "anyContains", new Class[]{String.class, String.class}, null);
String express = "取絕對值(-100); 轉換為大寫(\"hello world\"); 打印(\"你好嗎?\"); contains(\"helloworld\", \"aeiou\")";
Object result = runner.execute(express, context, null, false, false);
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
publicstaticclass BeanExample {
public static double abs(double value) {
System.out.println("取絕對值結果: " + value);
return Math.abs(value);
}
public static String upper(String abc) {
System.out.println("轉換為大寫結果: " + abc.toUpperCase());
return abc.toUpperCase();
}
public boolean anyContains(String str, String searchStr) {
char[] s = str.toCharArray();
for (char c : s) {
if (searchStr.contains(c + "")) {
returntrue;
}
}
returnfalse;
}
}
}
6.宏定義(macro)
在QLExpress中,宏定義(macro)允許將一個表達式片段命名為宏,并在其他地方引用這個宏。當需要在多個地方使用相同的復雜表達式時非常有用,可以提高代碼的可讀性和維護性。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: macro 宏定義
* @author: zhangyanfeng
**/
publicclass QLExpressMacroExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 定義宏
runner.addMacro("計算平均成績", "(語文+數學+英語)/3.0");
runner.addMacro("是否優秀", "計算平均成績>90");
// 設置變量值
context.put("語文", 88);
context.put("數學", 99);
context.put("英語", 95);
// 執行表達式并打印結果
Object result = runner.execute("是否優秀", context, null, false, false);
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.編譯腳本查詢外部需要定義的變量和函數
在QLExpress中,編譯腳本并查詢外部需要定義的變量和函數可以通過 ExpressRunner 提供的一些方法來實現。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: 編譯腳本,查詢外部需要定義的變量和函數。
* @author: zhangyanfeng
**/
publicclass QLExpressCompileExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner(true, true);
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("語文",120);
context.put("數學",23);
context.put("英語",23);
context.put("綜合考試",235);
// 定義腳本
String express = "double 平均分 = (語文 + 數學 + 英語 + 綜合考試) / 4.0; return 平均分";
// 查詢外部需要定義的變量
String[] variableNames = runner.getOutVarNames(express);
System.out.println("外部需要定義的變量:");
for (String variableName : variableNames) {
System.out.println("var : " + variableName);
}
// 查詢外部需要定義的函數
String[] functionNames = runner.getOutFunctionNames(express);
System.out.println("\n外部需要定義的函數:");
for (String functionName : functionNames) {
System.out.println("function : " + functionName);
}
// 編譯腳本并執行
Object result = runner.execute(express, context, null, false, false);
System.out.println("\n腳本執行結果: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
看下對應打印結果:
執行的表達式:double 平均分 = (語文 + 數學 + 英語 + 綜合考試) / 4.0; return 平均分
單詞分解結果:{double},{平均分},{=},{(},{語文},{+},{數學},{+},{英語},{+},{綜合考試},{)},{/},{4.0},{;},{return},{平均分}
預處理后結果:{double},{平均分},{=},{(},{語文},{+},{數學},{+},{英語},{+},{綜合考試},{)},{/},{4.0},{;},{return},{平均分}
單詞分析結果:double:CONST_CLASS,平均分:ID,=:=,(:(,語文:ID,+:+,數學:ID,+:+,英語:ID,+:+,綜合考試:ID,):),/:/,4.0:CONST_DOUBLE,;:;,return:return,平均分:ID
最后的語法樹:
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: =:= =
4: def:def def
5: double:CONST_CLASS CONST_CLASS
5: 平均分:CONST_STRING CONST
4: /:/ /
5: (:CHILD_EXPRESS CHILD_EXPRESS
6: +:+ +
7: +:+ +
8: +:+ +
9: 語文:ID ID
9: 數學:ID ID
8: 英語:ID ID
7: 綜合考試:ID ID
5: 4.0:CONST_DOUBLE CONST
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: return:returnreturn
4: 平均分:ID ID
1:LoadData Class:double
2:LoadData 平均分
3:OP : def OPNUMBER[2]
4:LoadAttr:語文
5:LoadAttr:數學
6:OP : + OPNUMBER[2]
7:LoadAttr:英語
8:OP : + OPNUMBER[2]
9:LoadAttr:綜合考試
10:OP : + OPNUMBER[2]
11:LoadData 4.0
12:OP : / OPNUMBER[2]
13:OP : = OPNUMBER[2]
14:clearDataStack
15:LoadAttr:平均分
16:return [value]
外部需要定義的變量:
var : 數學
var : 綜合考試
var : 英語
var : 語文
執行的表達式:double 平均分 = (語文 + 數學 + 英語 + 綜合考試) / 4.0; return 平均分
單詞分解結果:{double},{平均分},{=},{(},{語文},{+},{數學},{+},{英語},{+},{綜合考試},{)},{/},{4.0},{;},{return},{平均分}
預處理后結果:{double},{平均分},{=},{(},{語文},{+},{數學},{+},{英語},{+},{綜合考試},{)},{/},{4.0},{;},{return},{平均分}
單詞分析結果:double:CONST_CLASS,平均分:ID,=:=,(:(,語文:ID,+:+,數學:ID,+:+,英語:ID,+:+,綜合考試:ID,):),/:/,4.0:CONST_DOUBLE,;:;,return:return,平均分:ID
最后的語法樹:
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: =:= =
4: def:def def
5: double:CONST_CLASS CONST_CLASS
5: 平均分:CONST_STRING CONST
4: /:/ /
5: (:CHILD_EXPRESS CHILD_EXPRESS
6: +:+ +
7: +:+ +
8: +:+ +
9: 語文:ID ID
9: 數學:ID ID
8: 英語:ID ID
7: 綜合考試:ID ID
5: 4.0:CONST_DOUBLE CONST
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: return:returnreturn
4: 平均分:ID ID
1:LoadData Class:double
2:LoadData 平均分
3:OP : def OPNUMBER[2]
4:LoadAttr:語文
5:LoadAttr:數學
6:OP : + OPNUMBER[2]
7:LoadAttr:英語
8:OP : + OPNUMBER[2]
9:LoadAttr:綜合考試
10:OP : + OPNUMBER[2]
11:LoadData 4.0
12:OP : / OPNUMBER[2]
13:OP : = OPNUMBER[2]
14:clearDataStack
15:LoadAttr:平均分
16:return [value]
外部需要定義的函數:
執行的表達式:double 平均分 = (語文 + 數學 + 英語 + 綜合考試) / 4.0; return 平均分
單詞分解結果:{double},{平均分},{=},{(},{語文},{+},{數學},{+},{英語},{+},{綜合考試},{)},{/},{4.0},{;},{return},{平均分}
預處理后結果:{double},{平均分},{=},{(},{語文},{+},{數學},{+},{英語},{+},{綜合考試},{)},{/},{4.0},{;},{return},{平均分}
單詞分析結果:double:CONST_CLASS,平均分:ID,=:=,(:(,語文:ID,+:+,數學:ID,+:+,英語:ID,+:+,綜合考試:ID,):),/:/,4.0:CONST_DOUBLE,;:;,return:return,平均分:ID
最后的語法樹:
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: =:= =
4: def:def def
5: double:CONST_CLASS CONST_CLASS
5: 平均分:CONST_STRING CONST
4: /:/ /
5: (:CHILD_EXPRESS CHILD_EXPRESS
6: +:+ +
7: +:+ +
8: +:+ +
9: 語文:ID ID
9: 數學:ID ID
8: 英語:ID ID
7: 綜合考試:ID ID
5: 4.0:CONST_DOUBLE CONST
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: return:returnreturn
4: 平均分:ID ID
1:LoadData Class:double
2:LoadData 平均分
3:OP : def OPNUMBER[2]
4:LoadAttr:語文
5:LoadAttr:數學
6:OP : + OPNUMBER[2]
7:LoadAttr:英語
8:OP : + OPNUMBER[2]
9:LoadAttr:綜合考試
10:OP : + OPNUMBER[2]
11:LoadData 4.0
12:OP : / OPNUMBER[2]
13:OP : = OPNUMBER[2]
14:clearDataStack
15:LoadAttr:平均分
16:return [value]
腳本執行結果: 100.25
8.不定參數的使用
在QLExpress中,可以通過使用不定參數(動態參數)來處理方法的參數。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.DynamicParamsUtil;
import com.ql.util.express.ExpressRunner;
/**
* @program: zyfboot-javabasic
* @description: 不定參數的使用
* @author: zhangyanfeng
**/
publicclass QLExpressDynamicParamsExample {
public static void main(String[] args) {
try {
// 創建 ExpressRunner 實例
ExpressRunner runner = new ExpressRunner();
// 創建 DefaultContext 實例
DefaultContext<String, Object> expressContext = new DefaultContext<>();
// 在 runner 中添加一個函數,使用不定參數
runner.addFunctionOfServiceMethod("getTemplate", new QLExpressDynamicParamsExample(), "getTemplate",
new Class[]{Object[].class}, null);
// 調用 getTemplate 方法,傳遞數組作為參數
Object resultWithArray = runner.execute("getTemplate([11, '22', 33L, true])",
expressContext, null, false, false);
System.out.println("Result with Array: " + resultWithArray);
// 打開全局開關,啟用動態參數調用
DynamicParamsUtil.supportDynamicParams = true;
// 調用 getTemplate 方法,傳遞多個參數
Object resultWithDynamicParams = runner.execute("getTemplate(11, '22', 33L, true)", expressContext,
null, false, false);
System.out.println("Result with Dynamic Params: " + resultWithDynamicParams);
} catch (Exception e) {
e.printStackTrace();
}
}
// 等價于 getTemplate(Object[] params)
public Object getTemplate(Object... params) {
StringBuilder result = new StringBuilder();
for (Object obj : params) {
result.append(obj).append(",");
}
return result.toString();
}
}
9.集合的快捷用法
在QLExpress中,你可以使用一些快捷的語法來操作集合。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @program: zyfboot-javabasic
* @description: 集合操作
* @author: zhangyanfeng
**/
publicclass QLExpressCollectionOperationsExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 使用NewMap創建Map
String expressMap = "abc = NewMap(1:1, 2:2); return abc.get(1) + abc.get(2);";
Object resultMap = runner.execute(expressMap, context, null, false, false);
System.out.println("NewMap Result: " + resultMap);
// 使用NewList創建List
String expressList = "abc = NewList(1, 2, 3); return abc.get(1) + abc.get(2);";
Object resultList = runner.execute(expressList, context, null, false, false);
System.out.println("NewList Result: " + resultList);
// 使用方括號[]創建List
String expressSquareBrackets = "abc = [1, 2, 3]; return abc[1] + abc[2];";
Object resultSquareBrackets = runner.execute(expressSquareBrackets, context, null, false, false);
System.out.println("Square Brackets Result: " + resultSquareBrackets);
} catch (Exception e) {
e.printStackTrace();
}
}
}
10.集合的遍歷
類似java的語法,只是ql不支持for(obj:list){}的語法,只能通過下標訪問。
package org.zyf.javabasic.qlexpress;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import java.util.HashMap;
import java.util.Map;
/**
* @program: zyfboot-javabasic
* @description: 使用foreach關鍵字結合索引遍歷集合
* @author: zhangyanfeng
**/
publicclass QLExpressCollectionTraversalExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 創建一個Map
Map<String, String> map = new HashMap<>();
map.put("a", "a_value");
map.put("b", "b_value");
// 將Map放入上下文中
context.put("map", map);
// 遍歷Map
String express = "keySet = map.keySet();\n" +
"objArr = keySet.toArray();\n" +
"for (i = 0; i < objArr.length; i++) {\n" +
" key = objArr[i];\n" +
" System.out.println(map.get(key));\n" +
"}";
runner.execute(express, context, null, false, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、總結
本文全面介紹了 QLExpress 作為阿里巴巴開源的一款輕量級動態腳本引擎的主要功能和應用場景,并深入講解了其在規則引擎和業務場景中的使用優勢。通過對比其他規則引擎的性能與靈活性,QLExpress 展現了其在處理動態規則、復雜計算邏輯、實時決策等方面的獨特優勢,尤其適用于需要動態配置業務規則的場景。在實際應用中,QLExpress 的簡潔語法、快速上手和高性能表現,使其成為了企業動態業務處理的有效工具。
未來,QLExpress 還可以進一步優化和拓展,以支持更多場景和定制化功能。對于希望在系統中靈活應用規則邏輯、提升業務決策效率的開發者來說,QLExpress 是一個值得關注和嘗試的工具。
希望通過本文的介紹,大家能更好地理解和應用 QLExpress,從而在項目中實現更高效、更靈活的規則管理。