遇到的問題
最近項目中有這樣一種場景:
需要改變部分訂單的結算方式,這個改動點對交易結算影響很大,需要逐步切流以減少風險。
如果採用case by case硬編碼限定切流的場景來做,就很不靈活,單純這個切流就要上多次線。
因此有這樣的技術需求:使用一種靈活多變的切流方式,即可支持對按照訂單對象任何一個參數滿足某種條件時進行切流,如按照訂單類型字段、某些買家id符合要求。
解決方案
經過調研,最終採用aviator表達式來實現(輔以動態內容推送中間件diamond)。
一個簡單的demo便可滿足上述需求。
NCpsPaymentDTO paymentDTO = newNCpsPaymentDTO();
paymentDTO.setTkBizTag(5);
paymentDTO.setTbBuyerId(1234L);
ExtraInfo extraInfo = new ExtraInfo();
extraInfo.setEventId(1234567L);
HashMap<String, Object> paramMap= new HashMap<String, Object>();
//這裏賦值確保非null,保證配置的表達式可以不用再判空
paramMap.put("paymentDTO",paymentDTO);
paramMap.put("extraInfo",extraInfo);
String configInfo ="paymentDTO.tkBizTag == 5 && paymentDTO.tbBuyerId % 10000 <=2000 && extraInfo.eventId == 1234567";
Expression expression =AviatorEvaluator.compile(configInfo);
Boolean rst = (Boolean)expression.execute(paramMap);
System.out.println(rst); //true
Note:
其中configInfo取自動態內容推送中間件diamond。
瞭解到這個程度足夠了麼?No.關於aviator還需要知道得更多。
Aviator簡介
Aviator是一個高性能、輕量級的java語言實現的表達式求值引擎,主要用於各種表達式的動態求值。現在已經有很多開源可用的java表達式求值引擎,爲什麼還需要Avaitor呢?
Aviator的設計目標是輕量級和高性能,相比於Groovy、JRuby的笨重,Aviator非常小,加上依賴包也才450K,不算依賴包的話只有70K;當然,Aviator的語法是受限的,它不是一門完整的語言,而只是語言的一小部分集合。
其次,Aviator的實現思路與其他輕量級的求值器很不相同,其他求值器一般都是通過解釋的方式運行,而Aviator則是直接將表達式編譯成Java字節碼,交給JVM去執行。簡單來說,Aviator的定位是介於Groovy這樣的重量級腳本語言和IKExpression這樣的輕量級表達式引擎之間。
Aviator的特性
支持大部分運算操作符,包括算術操作符、關係運算符、邏輯操作符、正則匹配操作符(=~)、三元表達式?:,並且支持操作符的優先級和括號強制優先級,具體請看後面的操作符列表。
支持函數調用和自定義函數
支持正則表達式匹配,類似Ruby、Perl的匹配語法,並且支持類Ruby的$digit指向匹配分組。
自動類型轉換,當執行操作的時候,會自動判斷操作數類型並做相應轉換,無法轉換即拋異常。
支持傳入變量,支持類似a.b.c的嵌套變量訪問。
性能優秀
Aviator的限制
沒有if else、do while等語句,沒有賦值語句,僅支持邏輯表達式、算術表達式、三元表達式和正則匹配。
沒有位運算符
最新jar包
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>2.3.3</version>
</dependency>
Aviator用法
算術表達式
Long result = (Long)AviatorEvaluator.execute("1+2+3");
System.out.println(result); //6
note:Aviator的數值類型僅支持Long和Double,任何整數都將轉換成Long,任何浮點數都將轉換爲Double,包括用戶傳入的變量數值。
邏輯表達式
Boolean result2 = (Boolean)AviatorEvaluator.execute("3>1 && 2!=4 || true");
System.out.println(result2); //true
變量和字符串相加
Map<String, Object> env = newHashMap<String, Object>();
env.put("yourname","aviator");
String result3 = (String)AviatorEvaluator.execute(" 'hello ' + yourname ", env);
System.out.println(result3);
上面的例子演示了怎麼向表達式傳入變量值,表達式中的yourname是一個變量,默認爲null,通過傳入Map<String,Object>的變量綁定環境,將yourname設置爲你輸入的名稱。env的key是變量名,value是變量的值。
Aviator 2.2開始新增加一個exec方法,可以更方便地傳入變量並執行,而不需要構造env這個map了:
String result4 = (String) AviatorEvaluator.exec(" 'hello ' + yourname ","aviator2");
System.out.println(result4);
三元表達式
String result5 =(String)AviatorEvaluator.execute("3>0? 'yes':'no'");
System.out.println(result5);
函數調用
AviatorEvaluator.execute("string.length('hello')"); //求字符串長度
AviatorEvaluator.execute("string.contains('hello','h')"); //判斷字符串是否包含字符串AviatorEvaluator.execute("string.startsWith('hello','h')"); //是否以子串開頭AviatorEvaluator.execute("string.endsWith('hello','llo')"); 是否以子串結尾
AviatorEvaluator.execute("math.pow(-3,2)"); // 求n次方
AviatorEvaluator.execute("math.sqrt(14.0)"); //開 平方根
AviatorEvaluator.execute("math.sin(20)"); //正弦函數
還有一些更細緻的用法,詳情可以參考官方文檔:https://github.com/killme2008/aviator/wiki
參考文章
http://www.blogjava.net/killme2008/archive/2010/06/29/324758.html
http://blog.csdn.net/keda8997110/article/details/50782848