表示式引擎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
的數值型別僅支援Long
和Double
,
任何整數都將轉換成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 '
是一個Aviator
的String
, Aviator
的String
是任何用單引號或者雙引號括起來的字元序列, 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
並呼叫Expression
的execute
方法即可。
表示式中使用了括號來強制優先順序, 這個例子還使用了>
用於比較數值大小,
比較運算子!=、==、>、>=、<、<=
不僅可以用於數值,
也可以用於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.Map
中key
對應的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(