QLExpression學習使用教程

QLExpress

簡介

由阿里的電商業務規則、表達式(布爾組合)、特殊數學公式計算(高精度)、語法分析、腳本二次定製等強需求而設計的一門動態腳本引擎解析工具。

爲了解決當時電商規則動態編譯、表達式高精度計算、複雜布爾運算、自定義函數和操作符號、語法樹生成等需求而設計的。

支持特性

  • 1、線程安全,引擎運算過程中的產生的臨時變量都是threadlocal類型。
  • 2、高效執行,比較耗時的腳本編譯過程可以緩存在本地機器,運行時的臨時變量創建採用了緩衝池的技術,和groovy性能相當。
  • 3、弱類型腳本語言,和groovy,javascript語法類似,雖然比強類型腳本語言要慢一些,但是使業務的靈活度大大增強。
  • 4、安全控制,可以通過設置相關運行參數,預防死循環、高危系統api調用等情況。
  • 5、代碼精簡,依賴最小,250k的jar包適合所有java的運行環境,在android系統的低端pos機也得到廣泛運用。

Maven引入

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

例子github源碼

Github地址:QLExpressionStudy

圖分解

在這裏插入圖片描述

從語法樹分析、上下文、執行過程三個方面提供二次定製的功能擴展

提示

若要展示其中的編譯過程,將log4j.properties中的log4j.logger.com.imooc.mapper改成DEBUG。

例子

初次使用

參見 TestQLExpress 類的 testQLExpression函數:

   		ExpressRunner runner = new ExpressRunner();
        /**
         * 表達式計算的數據注入接口
         */
        DefaultContext<String, Object> context = new DefaultContext<String, Object>();
        context.put("a",1);
        context.put("b",2);
        context.put("c",3);
        String express = "a+b*c";//===> 定義規則,計算表達式等等
        Object r = runner.execute(express, context, null, true, true);// 解析規則+執行規則
        System.out.println(r);

Runner執行器設置

創建ExpressRunner的執行器示例時,可以設置下面兩個參數用以達到精度計算。

  • aIsPrecise 是否需要高精度計算支持
  • aIstrace 是否跟蹤執行指令的過程

執行器執行命令的設置

針對執行器的execute方法,可以設置如下參數。

  • expressString 程序文本,即要執行的表達式或規則
  • context 執行上下文。【IExpressContext對象(如果是Spring的Bean,則創建SpringBeanContext對象) 表示執行上下文】
  • errorList 輸出的錯誤信息List
  • isCache 是否使用Cache中的指令集,多次執行同一語句的情況下用以提高執行效率
  • isTrace 是否輸出詳細的執行指令信息
  • log 輸出的log

從簡單的例子來看,QLExpress的運行過程爲:

單詞分解–>單詞分析–>構建語法樹進行語法分析–>生成運行期指令集合–>執行生成的指令集合。

其中前4個過程涉及語法的匹配運算等非常耗時,可以設置execute方法的 isCache 是否使用Cache中的指令集參數,它可以緩存前四個過程。
即把 express語句在本地緩存中換成一段指令,第二次重複執行的時候直接執行指令,極大的提高了性能。

或者ExpressRunner設置成singleton(結合spring是非常容易做到的)。

支持普通的Java語法執行

運算符支持

運算符 備註 示例
+,-,*,/,<,>,<=,>=,==,!=,%,++,– mod等同於% a * b
in,like,&&,! in,like類似於sql語法 a in [1,2,3]
for,break、continue,if then else 不支持try{}catch{}
不支持java8的lambda表達式
不支持for循環集合操作
弱類型語言,請不要定義類型聲明,更不要用Templete
array的聲明不一樣
min,max,round,print,println,like,in 都是系統默認函數的關鍵字,請不要作爲變量名
int n=10;
int sum=0;
for(int i=0;i<n;i++){   sum=sum+i;}
return sum;

運算符分類

分類 運算符 示例
位運算 ^~&|<<>> 1<<2
四則運算 +-*/,%,++,-- 3%2
Boolean運算 !,<,>,<=,>=,==,!=,&&,|| 2==3
其他運算 =,?: 2>3?1:0
示例
n=10;
sum=0;
for(i=0;i<n;i++){   
	sum=sum+i;
}
return sum;

參見 TestQLExpress類的 testExpressForOperater函數。

a=1;
b=2;
maxnum = a>b?a:b;

參見 TestQLExpress類的 testTernaryOperator函數。

部分運算符列舉

運算符 描述 運算符 描述
mod %等同 for 循環語句控制符
return 進行值返回 if 條件語句控制符
in 類似sql語句的in then 與if同用
exportAlias 創建別名,並轉換爲全局別名 else 條件語句控制符
alias 創建別名 break 退出循環操作符
macro 定義宏 continue 繼續循環操作符
exportDef 將局部變量轉換爲全局變量 function 進行函數定義
like 類似sql語句的like new 創建一個對象
import 引入包或類,需在腳本最前頭 class 定義類
NewMap 創建Map NewList 創建集合

部分說明:

include:在一個表達式中引入其它表達式。例如: include com.taobao.upp.b; 資源的轉載可以自定義接口IExpressResourceLoader來實現,缺省是從文件中裝載
[]:匿名創建數組.int[][] abc = [[11,12,13],[21,22,23]];
NewMap:創建HashMap. Map abc = NewMap(1:1,2:2);Map abc = NewMap("a":1,"b":2)
NewList:串接ArrayList.List abc = NewList(1,2,3);
exportDef: 將局部變量轉換爲全局變量,例如:exportDef long userId
alias:創建別名,例如: alias 用戶ID user.userId
macro: 定義宏,例如: macro 降級  {level = level - 1}
in: 操作符號,例如: 3 in (3,4,5)
like:操作符號,例如: "abc" like ‘ab%‘

自定義系統函數

max:取最大值max(3,4,5)
min:最最小值min(2,9,1)
round:四捨五入round(19.08,1)
print:輸出信息不換行print("abc")
println:輸出信息並換行 println("abc")

樣例使用

表達式樣例
n=10;
for(sum=0,i=0;i<n;i++){
sum=sum+i;
}
return sum;

參見 TestQLExpress類的 testExpressForOperater 函數。

三目運算符樣例
a=1;
b=2;
maxnum = a>b?a:b;

參見 TestQLExpress類的 testTernaryOperator 函數。

數組定義
keys = new ArrayList();
deviceName2Value = new HashMap();
deviceNames = ["ng","si","umid","ut","mac","imsi","imei"];
mins = [5,30];

參見 GrammarTest 類的 TestArrayCreate 函數。

Java對象使用
object.amount*2+object.volume

參見 GrammarTest 類的 testJavaObjectOperate 函數。

import org.envision.tqw.study.BeanDefine.ObjectBean;
aObject = new ObjectBean();
aObject.setAmount(100);
aObject.volume = 20;
System.out.println("Volume:\n"+aObject.getVolume());

參見 GrammarTest 類的 testJavaObject2Operate 函數。

使用注意

  • 對象調用屬性賦值實際上是調用相應的Set,故而Set和Get方法不能爲私有。其次屬性私有並不影響調用。例如 aObject.volume = 20;相當於調用 aObject.setVolume(20);
  • 調用對象,確保能訪問到對象,即對象的訪問權限。此外,該對象所屬類需要先import,而且是在腳本定義之前部分【參考java類的使用,import語句一般位於定義類的文件前面部分】。
  • import 自定義類,默認會導入 import java.lang.*,import java.util.*;
數組遍歷
list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(i=0;i<list.size();i++){
    item = list.get(i);
    System.out.println(item);
}

參見 GrammarTest 類的 testArrayErgodicTest 函數。

map遍歷
map = new HashMap();
map.put("T","tan");
map.put("Q","qi");
map.put("W","wei");
keySet = map.keySet();
objArr = keySet.toArray();
for (i=0;i<objArr.length;i++) {
  key = objArr[i];
  System.out.println(map.get(key));
}

參見 GrammarTest 類的 testMapErgodicTest 函數。

定義函數

示例

function add(int a,int b){
  return a+b;
};

function sub(int a,int b){
  return a - b;
};

a=10;
return add(a,4) + sub(a,9);

參見 FunctionTest 類的 testFunction1 函數。

預定義裝載函數定義

裝載表達式,但不執行,例如一些宏定義,或者自定義函數。

runner.loadMutilExpress("",funExp);

操作符

中文操作符替換

如果  (語文+數學+英語>270) 則 {return 1;} 否則 {return 0;}

參見 OperatorTest 類的 testOperateReplace 函數。

二元操作符自定義

1 addT 22 addT 2

參見 OperatorTest 類的 testAddBinaryOperate 函數。

二元操作符定義示例

/**
 * @ClassName: BinaryOperator
 * @Description:
 *      定義二元操作符,該操作符實現
 *      a + ( b + b)
 * @Author: qiwei.tan
 * @Date: 2019/8/26 16:05
 * @Version: 1.0
 */
public class BinaryOperator extends Operator {


    public Object executeInner(Object[] list) throws Exception {
        int a = (Integer)list[0];
        int b = (Integer)list[1];
        return a + b + b;
    }
}
自定義操作符需注意
  • 自定義操作符類需繼承com.ql.util.express.Operator
  • 二元操作符和多元操作符考慮實現方式的不同

多元操作符

4 addN (1,2,3)

參見 OperatorTest 類的 testNElementOperate 函數。

多元操作符定義示例

/**
 * @ClassName: AddNOperator
 * @Description:
 *      多元操作符
 *      4 addN (1,2,3)
 *      4+1+2+3
 * @Author: qiwei.tan
 * @Date: 2019/8/29 15:35
 * @Version: 1.0
 */
public class AddNOperator extends Operator {

    public Object executeInner(Object[] list) throws Exception {
        int r = 0;
        for(int i=0;i<list.length;i++){
            r = r + (Integer)list[i];
        }
        return r;
    }
}

操作符的使用

1 join 2 join 3
1 + 2 + 3
join(1,2,3)

操作符定義

/**
 * @ClassName: JoinOperator
 * @Description:
 *      自定義連接操作符(二元或多元運算符【支持成函數列表調用】)
 *      1 join 2 join 3 ===> [1,2,3]
 *      返回ArrayList數組
 * @Author: qiwei.tan
 * @Date: 2019/8/29 11:39
 * @Version: 1.0
 */
public class JoinOperator extends Operator {
    public Object executeInner(Object[] list) throws Exception {
        Object opdata1 = list[0];
        Object opdata2 = list[1];
        if(opdata1 instanceof java.util.List){
            ((java.util.List)opdata1).add(opdata2);
            return opdata1;
        }else{
            java.util.List result = new java.util.ArrayList();
            result.add(opdata1);
            result.add(opdata2);
            if(list.length > 2){
                int index = -1;
                for(Object obj:list)
                    if(++index < 2)
                        continue;
                    else
                        result.add(obj);
            }
            return result;
        }
    }
}

官方Readme文件的定義有誤,無法支持當變成function時的多元操作符,故而更改。

addOperator

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

實現表達式中的詞“name”與自定義操作符的綁定

表達式:

1 join 2 join 3

參見 OperatorTest 類的 testOperateUserDefine 函數。

replaceOperator

runner.replaceOperator("+",new JoinOperator());

實現將內置的操作符定義變換爲自定義操作符定義

表達式:

1 + 2 + 3

參見 OperatorTest 類的 testOperateUserDefine2 函數。

addFunction

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

實現表達式轉換爲函數的使用,實際上是多元運算符的一種操作

表達式:

join(1,2,3)

參見 OperatorTest 類的 testOperateUserDefine3 函數。

小技巧

本部分不提供相應案例代碼

1、提供表達式上下文,屬性的值不需要在初始的時候全部加入,而是在表達式計算的時候,需要什麼信息才通過上下文接口獲取。

避免因爲不知道計算的需求,而在上下文中把可能需要的數據都加入。
runner.execute(“三星賣家 and 消保用戶”,errorList,true,expressContext) "三星賣家"和"消保用戶"的屬性是在需要的時候通過接口去獲取。

2、可以將計算結果直接存儲到上下文中供後續業務使用。例如:

  runner.execute("c = 1000 + 2000",errorList,true,expressContext); 
  //則在expressContext中會增加一個屬性c=3000,也可以在expressContext實現直接的數據庫操作等。

3、支持高精度浮點運算,只需要在創建執行引擎的時候指定參數即可:new ExpressRunner(true,false);
4、可以將Class和Spring對象的方法映射爲表達式計算中的別名,方便其他業務人員的立即和配置。例如

     //將 Math.abs() 映射爲 "取絕對值"。
      runner.addFunctionOfClassMethod("取絕對值", Math.class.getName(), "abs",new String[] { "double" }, null); 
      runner.execute("取絕對值(-5.0)",null,true,null); 

5、可以爲已經存在的boolean運算操作符號設置別名,增加錯誤信息同步輸出,在計算結果爲false的時候,同時返回錯誤信息,減少業務系統相關的處理代碼

   runner.addOperatorWithAlias("屬於", "in", "用戶$1不在允許的範圍")//用戶自定義的函數同樣也可以設置錯誤信息:例如: 
   runner.addFunctionOfClassMethod("isOk", BeanExample?.class.getName(),"isOk", new String[] { "String" }, "$1 不是VIP用戶"); 
  則在調用:
     List errorList = new ArrayList?(); 
     Object result =runner.execute("( (1+1) 屬於 (4,3,5)) and isOk("玄難")",errorList,true,null); 
    // 執行結果 result = false.同時在errorList中還會返回2個錯誤原因: 
    //     1、"用戶 2 不在允許的範圍"
    //     2、玄難 不是VIP用戶 

6、可以自定義函數,自定一個操作函數 group

class GroupOperator extends Operator {
	public GroupOperator(String aName) {
		this.name= aName;
	}
	public Object executeInner(Object[] list)throws Exception {
		Object result = new Integer(0);
		for (int i = 0; i < list.length; i++) {
			result = OperatorOfNumber.Add.execute(result, list[i]);
		}
		return result;
	}
}

則執行:

     runner.addFunction("累加", new GroupOperator("累加"));
     runner.addFunction("group", new GroupOperator("group"));
    //則執行:group(2,3,4)  = 9 ,累加(1,2,3)+累加(4,5,6)=21

7、可以自定操作符號。自定義的操作符號優先級設置爲最高。例如自定一個操作函數 love:

class LoveOperator extends Operator {	
	public LoveOperator(String aName) {
		this.name= aName;
	}
	public Object executeInner(Object[] list)
			throws Exception {
		String op1 = list[0].toString();
		String op2 = list[1].toString();
		String result = op2 +"{" + op1 + "}" + op2;		
		return result;
	}
}

然後增加到運算引擎:

 runner.addOperator("love", new LoveOperator("love"));
    //則執行:'a' love 'b' love 'c' love 'd' = "d{c{b{a}b}c}d"

8、可以重載已有的操作符號。例如替換“+”的執行邏輯。
9、可以延遲運算需要的數據
10、一個腳本可以調用其它腳本定義的宏和函數
11、可以類似VB的語法來使用操作符號和函數。print abc; 等價於 print(abc).
12、支持類定義
13、對 in 操作支持後面的是一個數組或者List變量

綁定java類或者對象的method

  • addFunctionOfClassMethod

        /**
         * 添加一個類的函數定義,例如:Math.abs(double) 映射爲表達式中的 "取絕對值(-5.0)"
         * @param name 函數名稱
         * @param aClassName 類名稱
         * @param aFunctionName 類中的方法名稱
         * @param aParameterTypes 方法的參數類型名稱
         * @param errorInfo 如果函數執行的結果是false,需要輸出的錯誤信息
         * @throws Exception
         */
         public void addFunctionOfClassMethod(String name, String aClassName,
    			String aFunctionName, String[] aParameterTypes, String errorInfo)
    			throws Exception{...}
    
  • addFunctionOfServiceMethod

    /**
     * 用於將一個用戶自己定義的對象(例如Spring對象)方法轉換爲一個表達式計算的函數
     * @param name
     * @param aServiceObject
     * @param aFunctionName
     * @param aParameterTypes
     * @param errorInfo
     * @throws Exception
     */
	public void addFunctionOfServiceMethod(String name, Object aServiceObject,
			String aFunctionName, String[] aParameterTypes, String errorInfo)
			throws Exception

兩種方法,無論是何種形式,啓用errorInfo一般都應該是boolean類型函數合適。

參見 GrammarTest 類的 testBindingClassOrObjectMethod 函數。

macro 宏定義

測試腳本

(語文+數學+英語)/3.0
計算平均成績>90
是否優秀

參見 GrammarTest 類的 testMacro 函數。

編譯腳本,查詢外部需要定義的變量和函數

腳本:

int 平均分 = (語文+數學+英語+綜合考試.科目2)/4.0;
return 平均分;

參見 GrammarTest 類的 testVarNeedDef 函數。

關於不定參數的使用

腳本:

getTemplate([11,'22',33L,true])
getTemplate(11,'22',33L,true)

參見 GrammarTest 類的 testMethodReplace 函數。

集合的快捷寫法

腳本

abc = NewMap(1:1,2:2); return abc.get(1) + abc.get(2);
abc = NewList(1,2,3); return abc.get(1)+abc.get(2)
abc = [1,2,3]; return abc[1]+abc[2];

參見 GrammarTest 類的 testSetCreateFast 函數。

其他相關

緩存性能比較

參見 PerfomanceTest 類的 testLocalCacheMutualImpact 函數。

檢查自定義的java類

running.checkSyntax(String text, boolean mockRemoteJavaClass, List<String> remoteJavaClassNames) 

參見 GrammarTest 類的 testCheckySyntax 函數。

是否將char類型識別爲String

runner的setIgnoreConstChar方法。

  • 是否忽略charset類型的數據,而識別爲string,比如 'a' -> "a"
  • 默認爲忽略,正常識別爲String【官方註釋此處有誤或實現有誤】

參見 GrammarTest 類的 testIgnoreConstChar 函數。

類定義

參見 GrammarTest 類的 TestClassDefine 函數和TestClassDefine2 函數。

位相關的定義

參見 GrammarTest 類的 testBitTest 函數。

類需導入才能使用

參見 GrammarTest 類的 testImportClassPath 函數。

關於棧的深度優化

參見 GrammarTest 類的 testStack 函數。

短路邏輯

針對runner的 isShortCircuit 屬性,來判斷,是否使用邏輯短路特性增強質量的效率

runner.setShortCircuit(false);

參見 GrammarTest 類的 testShortCircuit 函數和 testNoShortCircuit 函數。

場景

場景一

參見 SceneOne 類

在業務系統中存在一些邏輯判斷,例如 "商品A"只能是三星賣家,而且已經開通淘寶店鋪的用戶才能訂購
那麼我們期望業務人員看到的配置信息就是:“三星賣家 而且 已經開店”
則通過下列步驟可能實現這個功能:

定義一個用戶信息對象

class UserInfo {
    long id;
    long tag;
    String name;

    public UserInfo(long aId,String aName, long aUserTag) {
        this.id = aId;
        this.tag = aUserTag;
        this.name = aName;
    }
    public String getName(){
        return name;
    }
    public long getUserId() {
        return id;
    }

    public long getUserTag() {
        return tag;
    }
}

定義兩個基礎的功能函數

 /**
     * 判斷一個用戶TAG的第X位是否爲1。這個的demo,其實現合理性不考慮
     * @param user
     * @param tagBitIndex
     * @return
     */
    public boolean userTagJudge(UserInfo user,int tagBitIndex){
        boolean r =  (user.getUserTag() & ((long)Math.pow(2, tagBitIndex))) > 0;
        return r;
    }

    /**
     * 判斷一個用戶是否訂購過某個商品
     * @param user
     * @param goodsId
     * @return
     */
    public boolean hasOrderGoods(UserInfo user,long goodsId){
        //隨機模擬一個
        if(user.getUserId() % 2 == 1){
            return true;
        }else{
            return false;
        }
    }

初始化語句執行器

	public void initial() throws Exception{
		runner.addOperatorWithAlias("而且","and",null);
		runner.addFunctionOfClassMethod("userTagJudge", SceneOne .class.getName(), "userTagJudge",new String[] {UserInfo.class.getName(),"int"}, "你不是三星賣家");
		runner.addFunctionOfClassMethod("hasOrderGoods", SceneOne .class.getName(), "hasOrderGoods",new String[] {UserInfo.class.getName(),"long"}, "你沒有開通淘寶店鋪");
		runner.addMacro("三星賣家", "userTagJudge(userInfo,3)");//3表示三星賣家的標誌位
		runner.addMacro("已經開店", "hasOrderGoods(userInfo,100)");//100表示旺鋪商品的ID
	}

定義一個邏輯判斷函數

	/**
	 * 判斷邏輯執行函數
	 * @param userInfo
	 * @param expression
	 * @return
	 * @throws Exception
	 */
	public String hasPermission(UserInfo userInfo,String expression) throws Exception {			
        	IExpressContext<String,Object> expressContext = new DefaultContext<String,Object>();
    		expressContext.put("userInfo",userInfo);
    		List<String> errorInfo = new ArrayList<String>();
            Boolean result = (Boolean)runner.execute(expression, expressContext, errorInfo, true, false);
            String resultStr ="";
            if(result.booleanValue() == true){
            	resultStr = "可以訂購此商品";
            }else{
              for(int i=0;i<errorInfo.size();i++){
            	  if(i > 0){
            		  resultStr  = resultStr + "," ;
            	  }
            	  resultStr  = resultStr + errorInfo.get(i);
              }
              resultStr = resultStr  + ",所以不能訂購此商品";
            }
            return "親愛的" + userInfo.getName() + " : " + resultStr;
    }	

調用執行器執行判斷邏輯

	public static void main(String args[]) throws Exception{
		SceneOne demo = new SceneOne();
		demo.initial();
		System.out.println(demo.hasPermission(new UserInfo(100,"xuannan",7),  "三星賣家   而且   已經開店"));
		System.out.println(demo.hasPermission(new UserInfo(101,"qianghui",8), "三星賣家   而且   已經開店"));
		System.out.println(demo.hasPermission(new UserInfo(100,"張三",8), "三星賣家 and 已經開店"));
		System.out.println(demo.hasPermission(new UserInfo(100,"李四",7), "三星賣家 and 已經開店"));

場景二-審覈流程

簡單的流程管理,本場景涉及用於展示如何定義表達式,方法,並使用上下文變量。

腳本如下:

如果 (審批通過(經理,金額)){
   如果  (金額  大於 5000){ 
     如果  (審批通過(總監,金額)){
        如果  (審批通過(財務,金額)){
           報銷入賬(金額)
        }否則  {
            打回修改(申請人)
        }
     }否則 {
        打回修改(申請人)
     }
   }否則  {
      如果  (審批通過(財務,金額)){
        報銷入賬(金額)
      }否則 {
         打回修改(申請人)
      }
   }
}否則 {
   打回修改(申請人)
}
打印("完成");

查看SceneTwo

場景三-物流分配

非加權:

費用科目(物流訂單.倉儲TP,"倉儲費")= 物流訂單.重量 * 0.5 ;

if(物流訂單.重量  > 5) then{ 
       費用科目(物流訂單.物流TP,"運輸費")= 3.0 + (物流訂單.重量 - 5 ) * 1.5 ; 
} else { 
      費用科目(物流訂單.物流TP,"運輸費")= 3.0; 
};
費用科目(物流訂單.包裝TP,"包裝費")= 物流訂單.重量 * 2.5 ; 

加權:

費用科目(物流訂單.倉儲TP,"倉儲費")= 物流訂單.重量 * 0.5 ;

if(物流訂單.重量  > 5) then{
       費用科目(物流訂單.物流TP,"運輸費")= 3.0 + (物流訂單.重量 - 5 ) * 1.5 ;
} else {
      費用科目(物流訂單.物流TP,"運輸費")= 3.0;
};
費用科目(物流訂單.包裝TP,"包裝費")= 物流訂單.重量 * 2.5 ;

查看SceneThree

示例來源於官方文案

總結

  • QLExpress屬於弱類型腳本語言,一般不推薦聲明局部變量的類型。語法編譯階段不會做類型校驗,也不會做方法的參數校驗,所以很靈活。
  • QLExpress的這套自定義的指令集屬於解析執行,RunEnvironment中通過programPoint函數指針的跳轉來實現每條指令的逐個計算,通過dataContainer作爲棧來存儲中間計算結果。
  • QLExpress定義的指令類型比較少,粒度比較粗,但是足夠完成所有的語法功能。
  • QLExpress整個運算過程是通過threadLocal來保證線程安全的。
  • QLExpress的腳本編譯過程比較耗時,但是是上下文無關的,所以同一個腳本運行緩存之後性能提升非常明顯。
  • QLExpress指令計算運算過程中,基本不會new新對象,是通過緩存池技術來儘量減少資源的消耗。
  • QLExpress的宏,function,Operator,變量是非常開放的,名字可以爲中文字符,也可以隨意擴展,還可以通過腳本靜態分析出包含哪些變量、函數,很方便的進行二次業務開發。
  • 腳本調用classLoader資源的時候可以import,也可以使用類的全路徑,構造函數、靜態方法、對象方法、類的字段、函數的不定參數調用統統搞定。

參考文獻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章