1. 程式人生 > >表示式引擎aviator

表示式引擎aviator

Aviator是一個輕量級、高效能的Java表示式執行引擎, 本文內容主要來自於官方文件

簡介

Aviator是一個高效能、輕量級的 java 語言實現的表示式求值引擎, 主要用於各種表示式的動態求值。現在已經有很多開源可用的 java 表示式求值引擎,為什麼還需要 Avaitor 呢?
Aviator的設計目標是輕量級和高效能,相比於Groovy、JRuby的笨重, Aviator非常小, 加上依賴包也才450K,不算依賴包的話只有 70K; 當然, Aviator的語法是受限的, 它不是一門完整的語言, 而只是語言的一小部分集合。
其次, Aviator的實現思路與其他輕量級的求值器很不相同, 其他求值器一般都是通過解釋的方式執行, 而Aviator則是直接將表示式編譯成Java 位元組碼, 交給JVM去執行。簡單來說, Aviator的定位是介於Groovy這樣的重量級指令碼語言和IKExpression這樣的輕量級表示式引擎 之間。
Aviator支援大部分運算操作符, 包括算術操作符、關係運算符、邏輯操作符、位運算子、正則匹配操作符(=~

)、三元表示式(?:), 並且支援操作符的優先順序和括號強制優先順序, 具體請看後面的操作符列表, 支援自定義函式.

包依賴

Aviator依賴了commons-beanutils, 使用Aviator可以新增下面的maven依賴:

<dependency>
    <groupId>com.googlecode.aviator</groupId>
    <artifactId>aviator</artifactId>
    <version>2.3.3</version>
</dependency>

使用手冊

執行表示式

Aviator的使用都是集中通過com.googlecode.aviator.AviatorEvaluator這個入口類來處理, 最簡單的例子, 執行一個計算1+2+3的表示式:

import com.googlecode.aviator.AviatorEvaluator;
public class TestAviator {
    public static void main(String[] args) {
        Long result = (Long) AviatorEvaluator.execute("1+2+3"
); System.out.println(result); } }

細心的朋友肯定注意到結果是Long,而不是Integer。這是因為Aviator的數值型別僅支援LongDouble, 任何整數都將轉換成Long, 任何浮點數都將轉換為Double, 包括使用者傳入的變數數值。這個例子的列印結果將是正確答案6

使用變數

想讓Aviator對你say hello嗎? 很簡單, 傳入你的名字, 讓Aviator負責字串的相加:

public class TestAviator {
    public static void main(String[] args) {
        String yourName = "Michael";
        Map<String, Object> env = new HashMap<String, Object>();
        env.put("yourName", yourName);
        String result = (String) AviatorEvaluator.execute(" 'hello ' + yourName ", env);
        System.out.println(result);  // hello Michael
    }
}

上面的例子演示了怎麼向表示式傳入變數值, 表示式中的yourName是一個變數, 預設為null, 通過傳入Map<String,Object>的變數繫結環境, 將yourName設定為你輸入的名稱。 env 的key是變數名, value是變數的值。
上面例子中的'hello '是一個AviatorStringAviatorString是任何用單引號或者雙引號括起來的字元序列, String可以比較大小(基於unicode順序), 可以參與正則匹配, 可以與任何物件相加, 任何物件與String相加結果為String。 String中也可以有轉義字元,如\n、\\、\' 等。

AviatorEvaluator.execute(" 'a\"b' ");           // 字串 a"b
AviatorEvaluator.execute(" \"a\'b\" ");         // 字串 a'b
AviatorEvaluator.execute(" 'hello ' + 3 ");     // 字串 hello 3
AviatorEvaluator.execute(" 'hello '+ unknow "); // 字串 hello null

exec 方法

Aviator 2.2 開始新增加一個exec方法, 可以更方便地傳入變數並執行, 而不需要構造env這個map了:

String name = "dennis";
AviatorEvaluator.exec(" 'hello ' + yourName ", name); // hello dennis

只要在exec中按照變數在表示式中的出現順序傳入變數值就可以執行, 不需要構建Map了。

呼叫函式

Aviator 支援函式呼叫, 函式呼叫的風格類似 lua, 下面的例子獲取字串的長度:

AviatorEvaluator.execute("string.length('hello')");  // 5

string.length('hello')是一個函式呼叫, string.length是一個函式, 'hello'是呼叫的引數。
再用string.substring來擷取字串:

AviatorEvaluator.execute("string.contains(\"test\", string.substring('hello', 1, 2))");  // true

通過string.substring('hello', 1, 2)獲取字串'e', 然後通過函式string.contains判斷e是否在'test'中。可以看到, 函式可以巢狀呼叫。
Aviator 的內建函式列表請看後面。

自定義函式

Aviator 除了內建的函式之外,還允許使用者自定義函式,只要實現com.googlecode.aviator.runtime.type.AviatorFunction介面, 並註冊到AviatorEvaluator即可使用. AviatorFunction介面十分龐大, 通常來說你並不需要實現所有的方法, 只要根據你的方法的參 數個數, 繼承AbstractFunction類並override相應方法即可。

可以看一個例子,我們實現一個add函式來做數值的相加:

public class TestAviator {
    public static void main(String[] args) {
        //註冊函式
        AviatorEvaluator.addFunction(new AddFunction());
        System.out.println(AviatorEvaluator.execute("add(1, 2)"));           // 3.0
        System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0
    }
}
class AddFunction extends AbstractFunction {
    @Override
    public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
        Number left = FunctionUtils.getNumberValue(arg1, env);
        Number right = FunctionUtils.getNumberValue(arg2, env);
        return new AviatorDouble(left.doubleValue() + right.doubleValue());
    }
    public String getName() {
        return "add";
    }
}

註冊函式通過AviatorEvaluator.addFunction方法, 移除可以通過removeFunction

編譯表示式

上面提到的例子都是直接執行表示式, 事實上 Aviator 背後都幫你做了編譯並執行的工作。 你可以自己先編譯表示式, 返回一個編譯的結果, 然後傳入不同的env來複用編譯結果, 提高效能, 這是更推薦的使用方式:

public class TestAviator {
    public static void main(String[] args) {
        String expression = "a-(b-c)>100";
        // 編譯表示式
        Expression compiledExp = AviatorEvaluator.compile(expression);
        Map<String, Object> env = new HashMap<String, Object>();
        env.put("a", 100.3);
        env.put("b", 45);
        env.put("c", -199.100);
        // 執行表示式
        Boolean result = (Boolean) compiledExp.execute(env);
        System.out.println(result);  // false
    }
}

通過compile方法可以將表示式編譯成Expression的中間物件, 當要執行表示式的時候傳入env並呼叫Expressionexecute方法即可。 表示式中使用了括號來強制優先順序, 這個例子還使用了>用於比較數值大小, 比較運算子!=、==、>、>=、<、<=不僅可以用於數值, 也可以用於String、Pattern、Boolean等等, 甚至是任何使用者傳入的兩個都實現了java.lang.Comparable介面的物件之間。

編譯後的結果你可以自己快取, 也可以交給 Aviator 幫你快取, AviatorEvaluator內部有一個全域性的快取池, 如果你決定快取編譯結果, 可以通過:

public static Expression compile(String expression, boolean cached)

cached設定為true即可, 那麼下次編譯同一個表示式的時候將直接返回上一次編譯的結果。
使快取失效通過:

public static void invalidateCache(String expression)

方法。

訪問陣列和集合

可以通過中括號去訪問陣列和java.util.List物件, 可以通過map.key訪問java.util.Mapkey對應的value, 一個例子:

public static void main(String[] args) {
    final List<String> list = new ArrayList<String>();
    list.add("hello");
    list.add(" world");
    final int[] array = new int[3];
    array[0] = 0;
    array[1] = 1;
    array[2] = 3;
    final Map<String, Date> map = new HashMap<String, Date>();
    map.put(