QLExpress使用說明

一個(gè)輕量級(jí)的類java語法規(guī)則引擎,作為一個(gè)嵌入式規(guī)則引擎在業(yè)務(wù)系統(tǒng)中使用。讓業(yè)務(wù)規(guī)則定義簡便而不失靈活。讓業(yè)務(wù)人員就可以定義業(yè)務(wù)規(guī)則。支持標(biāo)準(zhǔn)的JAVA語法,還可以支持自定義操作符號(hào)、操作符號(hào)重載、函數(shù)定義、宏定義、數(shù)據(jù)延遲加載等。

QLExpress在阿里集團(tuán)內(nèi)部有很強(qiáng)的影響力,被廣泛應(yīng)用在電商場景中,在業(yè)務(wù)靈活多變的場景下常常需要規(guī)則引擎的支持。

官方地址為:https://github.com/alibaba/QLExpress

項(xiàng)目已經(jīng)給了很多的使用案例,但是如果不親自運(yùn)行一下,不容易體會(huì)到其用法與強(qiáng)大之處,本次主要給一個(gè)QLExpress的運(yùn)行案例,可以真正的跑起來官方給的各種案例代碼。

案例

說明:

/**
 * 執(zhí)行一段文本
 * @param expressString 程序文本
 * @param context 執(zhí)行上下文,執(zhí)行上下文在執(zhí)行的過程中會(huì)產(chǎn)生中間數(shù)據(jù),上下文不要重復(fù)使用
 * @param errorList 輸出的錯(cuò)誤信息List
 * @param isCache 是否使用Cache中的指令集
 * @param isTrace 是否輸出詳細(xì)的執(zhí)行指令信息
 * @return
 * @throws Exception
 */
   public Object execute(String expressString, IExpressContext<String,Object> context,
         List<String> errorList, boolean isCache, boolean isTrace) throws Exception {
      return this.execute(expressString, context, errorList, isCache, isTrace, null);
   }

運(yùn)行案例:

package me.aihe.demo.qlexpress;

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


import java.util.HashMap;

public class QLExpressionDemo {
    public static void main(String[] args) throws Exception {
        ExpressRunner runner = new ExpressRunner();
//        getStarted(runner);

//        basicStatement(runner);

//        collectionStatement(runner);

//        objectStatement(runner);

//        functionStatement(runner);

//        macronStatement(runner);

//        workWithJavaStatement(runner);

//        extendOperatorStatement(runner);

        customOperatorStatement(runner);
    }

    /**
     * 自定義操作符的使用
     * @param runner
     */
    private static void customOperatorStatement(ExpressRunner runner) throws Exception {
         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);
                    return result;
                }
            }
        }

        // 返回結(jié)果  [1, 2, 3]
        //(1)addOperator
        DefaultContext<String, Object> context = new DefaultContext<String, Object>();
        runner.addOperator("join",new JoinOperator());
        Object r = runner.execute("1 join 2 join 3", context, null, false, false);
        System.out.println(r);

        // (2) addFunction
        // 返回結(jié)果:[1 , 2 ]
        runner.addFunction("joinfunc",new JoinOperator());
        r = runner.execute("joinfunc(1,2,3)", context, null, false, false);
        System.out.println(r);

        //(3)replaceOperator
        // 返回結(jié)果  [1, 2, 3]
        runner.replaceOperator("+",new JoinOperator());
        r = runner.execute("1 + 2 + 3", context, null, false, false);
        System.out.println(r);


    }

    /**
     * 操作符處理,一般不太常用,但可以自定義一些操作符
     * @param runner
     */
    private static void extendOperatorStatement(ExpressRunner runner) throws Exception {
        runner.addOperatorWithAlias("如果", "if",null);
        runner.addOperatorWithAlias("則", "then",null);
        runner.addOperatorWithAlias("否則", "else",null);

        IExpressContext<String, Object> context =new DefaultContext<String, Object>();
        context.put("語文", 88);
        context.put("數(shù)學(xué)", 99);
        context.put("英語", 95);

        String exp = "如果  (語文+數(shù)學(xué)+英語>270) 則 {return 1;} 否則 {return 0;}";
//        DefaultContext<String, Object> context = new DefaultContext<String, Object>();
        Object result = runner.execute(exp, context, null, false, false, null);
        System.out.println(result);
    }

    /**
     * 將Java中已經(jīng)寫好的一些方法,綁定到我們自定義的變量上,在業(yè)務(wù)中最常用的部分。
     * @param runner
     */
    private static void workWithJavaStatement(ExpressRunner runner) throws Exception {
        // 在使用的時(shí)候會(huì)創(chuàng)建對(duì)象
        runner.addFunctionOfClassMethod("取絕對(duì)值", Math.class.getName(), "abs",
                new String[] { "double" }, null);

        // 對(duì)象已經(jīng)存在,直接調(diào)用對(duì)象中的方法
        runner.addFunctionOfServiceMethod("打印", System.out, "println",new String[] { "String" }, null);

        String exp = "a=取絕對(duì)值(-100);打印(\"Hello World\");打印(a.toString())";
        DefaultContext<String, Object> context = new DefaultContext<>();
        runner.execute(exp, context,null,false,false);
        System.out.println(context);
    }

    /**
     * Macro定義, 即預(yù)先定義一些內(nèi)容,在使用的時(shí)候直接替換Macro中的變量為上下文的內(nèi)容
     * @param runner
     */
    private static void macronStatement(ExpressRunner runner) throws Exception {
        runner.addMacro("計(jì)算平均成績", "(語文+數(shù)學(xué)+英語)/3.0");
        runner.addMacro("是否優(yōu)秀", "計(jì)算平均成績>90");

        IExpressContext<String, Object> context =new DefaultContext<String, Object>();
        context.put("語文", 88);
        context.put("數(shù)學(xué)", 99);
        context.put("英語", 95);
        Object result = runner.execute("是否優(yōu)秀", context, null, false, false);
        System.out.println(result);
    }

    /**
     * 自定義在QLexpress中的函數(shù)
     * 一般語句的最后一句話是返回結(jié)果
     * @param runner
     */
    private static void functionStatement(ExpressRunner runner) throws Exception {
        String functionStatement = "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" +
                "add(a,4) + sub(a,9);";

        Object result = runner.execute(functionStatement, new DefaultContext<>(), null, false, false);
//        runner.execute(functionStatement, new DefaultContext<>(), null, true, false, 1000);
        System.out.println(result);
    }

    /**
     * 對(duì)Java對(duì)象的操作
     * 系統(tǒng)自動(dòng)會(huì)import java.lang.*,import java.util.*;
     * @param runner
     */
    private static void objectStatement(ExpressRunner runner) throws Exception {
//        TradeEvent tradeEvent = new TradeEvent();
//        tradeEvent.setPrice(20.0);
//        tradeEvent.setName("購物");
//        tradeEvent.setId(UUID.randomUUID().toString());//
//
        String objectStatement = "import me.aihe.demo.trade.TradeEvent;\n" +
                "        tradeEvent = new TradeEvent();\n" +
                "        tradeEvent.setPrice(20.0);\n" +
                "        tradeEvent.id=UUID.randomUUID().toString();\n" +
                "        System.out.println(tradeEvent.getId());\n" +
                "        System.out.println(tradeEvent.price);";
        runner.execute(objectStatement, new DefaultContext<>(), null, false, false);

    }

    /**
     * 在定義集合,數(shù)組相關(guān)的操作,ql有自己的語法,不能簡單的使用Java語法
     * //java語法:使用泛型來提醒開發(fā)者檢查類型
     * keys = new ArrayList<String>();
     * deviceName2Value = new HashMap<String,String>(7);
     * String[] deviceNames = {"ng","si","umid","ut","mac","imsi","imei"};
     * int[] mins = {5,30};
     *
     * //ql寫法:
     * keys = new ArrayList();
     * deviceName2Value = new HashMap();
     * deviceNames = ["ng","si","umid","ut","mac","imsi","imei"];
     * mins = [5,30];
     *
     *
     * //java語法:對(duì)象類型聲明
     * FocFulfillDecisionReqDTO reqDTO = param.getReqDTO();
     * //ql寫法:
     * reqDTO = param.getReqDTO();
     *
     * //java語法:數(shù)組遍歷
     * for(GRCRouteLineResultDTO item : list) {
     * }
     * //ql寫法:
     * for(i=0;i<list.size();i++){
     * item = list.get(i);
     * }
     *
     * //java語法:map遍歷
     * for(String key : map.keySet()) {
     *   System.out.println(map.get(key));
     * }
     * //ql寫法:
     *   keySet = map.keySet();
     *   objArr = keySet.toArray();
     *   for (i=0;i<objArr.length;i++) {
     *   key = objArr[i];
     *    System.out.println(map.get(key));
     *   }
     * @param runner
     */
    private static void collectionStatement(ExpressRunner runner) throws Exception {
        DefaultContext<String, Object> defaultContext = new DefaultContext<>();
        HashMap<String, String> mapData = new HashMap(){{
            put("a","hello");
            put("b","world");
            put("c","!@#$");
        }};
        defaultContext.put("map",mapData);
        //ql不支持for(obj:list){}的語法,只能通過下標(biāo)訪問。
        String mapTraverseStatement = " 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(mapTraverseStatement,defaultContext,null,false,false);
        System.out.println(defaultContext);

        // 集合的快捷寫法
        /**
         *       ExpressRunner runner = new ExpressRunner(false,false);
         *         DefaultContext<String, Object> context = new DefaultContext<String, Object>();
         *         String express = "abc = NewMap(1:1,2:2); return abc.get(1) + abc.get(2);";
         *         Object r = runner.execute(express, context, null, false, false);
         *         System.out.println(r);
         *         express = "abc = NewList(1,2,3); return abc.get(1)+abc.get(2)";
         *         r = runner.execute(express, context, null, false, false);
         *         System.out.println(r);
         *         express = "abc = [1,2,3]; return abc[1]+abc[2];";
         *         r = runner.execute(express, context, null, false, false);
         *         System.out.println(r);
         */

    }

    /**
     * 語法基本說明:
     * 不支持try{}catch{}
     * 不支持java8的lambda表達(dá)式
     * 不支持for循環(huán)集合操作for (GRCRouteLineResultDTO item : list)
     * 弱類型語言,請(qǐng)不要定義類型聲明
     * 不要用Templete(Map<String,List>之類的)
     * array的聲明不一樣
     * min,max,round,print,println,like,in 都是系統(tǒng)默認(rèn)函數(shù)的關(guān)鍵字,請(qǐng)不要作為變量名
     * @param runner
     */
    private static void basicStatement(ExpressRunner runner) throws Exception {
        DefaultContext<String, Object> defaultContext = new DefaultContext<>();

//        defaultContext.put("n",10);  //直接從Java中傳遞上下文等于在表達(dá)式中傳遞上下文
        String loopStatement = "sum=0;n=10;" +
                "for(i=0;i<n;i++){\n" +
                "sum=sum+i;\n" +
                "}\n" +
                "return sum;";
        Object result = runner.execute(loopStatement, defaultContext, null, false, false);
        System.out.println(result);


        // 注意使用同一個(gè)defaultContext,上一步語句執(zhí)行的中間變量會(huì)被傳遞到下一個(gè)語句中
        String maxmiumStatement = "a=1;\n" +
                "b=2;\n" +
                "maxnum = a>b?a:b;";
        result = runner.execute(maxmiumStatement, defaultContext, null, false, false);
        System.out.println(result);

    }

    // 入門展示express使用部分
    private static void getStarted(ExpressRunner runner) throws Exception {
        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);
    }
}

image-20191011080218251

說明

綁定java類或者對(duì)象的method,在業(yè)務(wù)中經(jīng)常用到,可以重點(diǎn)關(guān)注,其余的親自跑一下看看運(yùn)行效果。

參考:

https://github.com/alibaba/QLExpress

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容