Java各種規則引擎
一. Drools規則引擎
- 簡介:
Drools就是為了解決業務代碼和業務規則分離的引擎。
Drools 規則是在 Java 應用程序上運行的,其要執行的步驟順序由代碼確定
,為了實現這一點,Drools 規則引擎將業務規則轉換成執行樹。
- 特性:
優點:
1、簡化系統架構,優化應用
2、提高系統的可維護性和維護成本
3、方便系統的整合
4、減少編寫“硬代碼”業務規則的成本和風險
3.原理:
- 使用方式:
(1)Maven 依賴:
<dependencies> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>6.5.0.Final</version> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-compiler</artifactId> <version>6.5.0.Final</version> <scope>runtime</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
(2)新建配置文件/src/resources/META-INF/kmodule.xml
<?xml version="1.0" encoding="UTF-8"?> <kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"> <kbase name="rules" packages="rules"> <ksession name="myAgeSession"/> </kbase> </kmodule>
(3)新建drools規則文件/src/resources/rules/age.drl
import com.lrq.wechatDemo.domain.User // 導入類 dialect "mvel" rule "age" // 規則名,唯一 when $user : User(age<15 || age>60) //規則的條件部分 then System.out.println("年齡不符合要求!"); end
工程搭建完畢,效果如圖:
測試用例:
/** * CreateBy: haleyliu * CreateDate: 2018/12/26 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath*:applicationContext.xml"}) public class TestUser { private static KieContainer container = null; private KieSession statefulKieSession = null; @Test public void test(){ KieServices kieServices = KieServices.Factory.get(); container = kieServices.getKieClasspathContainer(); statefulKieSession = container.newKieSession("myAgeSession"); User user = new User("duval yang",12); statefulKieSession.insert(user); statefulKieSession.fireAllRules(); statefulKieSession.dispose(); } }
二.Aviator表達式求值引擎
- 簡介:
Aviator是一個高性能、輕量級的java語言實現的表達式求值引擎,主要用於各
種表達式的動態求值。現在已經有很多開源可用的java表達式求值引擎,為什
么還需要Avaitor呢?
Aviator的設計目標是輕量級和高性能 ,相比於Groovy、JRuby的笨重,Aviator
非常小,加上依賴包也才450K,不算依賴包的話只有70K;當然,Aviator的語法
是受限的,它不是一門完整的語言,而只是語言的一小部分集合。
其次,Aviator的實現思路與其他輕量級的求值器很不相同,其他求值器一般都
是通過解釋的方式運行,而Aviator則是直接將表達式編譯成Java字節碼,交給
JVM去執行。簡單來說,Aviator的定位是介於Groovy這樣的重量級腳本語言和
IKExpression這樣的輕量級表達式引擎之間。
- 特性:
(1)支持大部分運算操作符,包括算術操作符、關系運算符、邏輯操作符、
正則匹配操作符(=~)、三元表達式?: ,並且支持操作符的優先級和括號強制優
先級,具體請看后面的操作符列表。
(2)支持函數調用和自定義函數。
(3)支持正則表達式匹配,類似Ruby、Perl的匹配語法,並且支持類Ruby的
$digit指向匹配分組。自動類型轉換,當執行操作的時候,會自動判斷操作數類
型並做相應轉換,無法轉換即拋異常。
(4)支持傳入變量,支持類似a.b.c的嵌套變量訪問。
(5)性能優秀。
(6)Aviator的限制,沒有if else、do while等語句,沒有賦值語句,僅支持邏
輯表達式、算術表達式、三元表達式和正則匹配。沒有位運算符
-
整體結構:
整體結構.png -
maven依賴:
<dependency> <groupId>com.googlecode.aviator</groupId> <artifactId>aviator</artifactId> <version>${aviator.version}</version> </dependency>
- 執行方式
執行表達式的方法有兩個:execute()、exec();
execute(),需要傳遞Map格式參數
exec(),不需要傳遞Map
示例:
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { // exec執行方式,無需傳遞Map格式 String age = "18"; System.out.println(AviatorEvaluator.exec("'His age is '+ age +'!'", age)); // execute執行方式,需傳遞Map格式 Map<String, Object> map = new HashMap<String, Object>(); map.put("age", "18"); System.out.println(AviatorEvaluator.execute("'His age is '+ age +'!'", map)); } }
-
使用函數
Aviator可以使用兩種函數:內置函數、自定義函數
(1)內置函數
Aviator內置函數.png
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { Map<String,Object> map = new HashMap<>(); map.put("s1","123qwer"); map.put("s2","123"); System.out.println(AviatorEvaluator.execute("string.startsWith(s1,s2)",map)); } }
(2)自定義函數
自定義函數要繼承AbstractFunction類,重寫目標方法。
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { // 注冊自定義函數 AviatorEvaluator.addFunction(new MultiplyFunction()); // 方式1 System.out.println(AviatorEvaluator.execute("multiply(12.23, -2.3)")); // 方式2 Map<String, Object> params = new HashMap<>(); params.put("a", 12.23); params.put("b", -2.3); System.out.println(AviatorEvaluator.execute("multiply(a, b)", params)); } } class MultiplyFunction extends AbstractFunction{ @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) { double num1 = FunctionUtils.getNumberValue(arg1, env).doubleValue(); double num2 = FunctionUtils.getNumberValue(arg2, env).doubleValue(); return new AviatorDouble(num1 * num2); } @Override public String getName() { return "multiply"; } }
-
常用操作符的使用
(1)操作符列表
操作符列表.png
(2)常量和變量
(3)編譯表達式
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { String expression = "a+(b-c)>100"; // 編譯表達式 Expression compiledExp = AviatorEvaluator.compile(expression); Map<String, Object> env = new HashMap<>(); env.put("a", 100.3); env.put("b", 45); env.put("c", -199.100); // 執行表達式 Boolean result = (Boolean) compiledExp.execute(env); System.out.println(result); } }
(4) 訪問數組和集合
List和數組用list[0]和array[0],Map用map.date
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { final List<String> list = new ArrayList<>(); 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<>(); map.put("date", new Date()); Map<String, Object> env = new HashMap<>(); env.put("list", list); env.put("array", array); env.put("map", map); System.out.println(AviatorEvaluator.execute( "list[0]+':'+array[0]+':'+'today is '+map.date", env)); } }
(5) 三元比較符
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { Map<String, Object> env = new HashMap<String, Object>(); env.put("a", -5); String result = (String) AviatorEvaluator.execute("a>0? 'yes':'no'", env); System.out.println(result); } }
(6) 正則表達式匹配
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { String email = "hello2018@gmail.com"; Map<String, Object> env = new HashMap<String, Object>(); env.put("email", email); String username = (String) AviatorEvaluator.execute("email=~/([\\w0-8]+)@\\w+[\\.\\w+]+/ ? $1 : 'unknow' ", env); System.out.println(username); } }
(7) 變量的語法糖衣
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { User user = new User(1,"jack","18"); Map<String, Object> env = new HashMap<>(); env.put("user", user); String result = (String) AviatorEvaluator.execute(" '[user id='+ user.id + ',name='+user.name + ',age=' +user.age +']' ", env); System.out.println(result); } } /** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class User { private int id; private String name; private String age; public User() { } public User(int id, String name, String age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age='" + age + '\'' + '}'; } }
(8) nil對象[任何對象都比nil大除了nil本身]
nil是Aviator內置的常量,類似java中的null,表示空的值。nil跟null不同的在 於,在java中null只能使用在==、!=的比較運算符,而nil還可以使用>、>=、 <、<=等比較運算符。Aviator規定,[任何對象都比nil大除了nil本身]。用戶傳入 的變量如果為null,將自動以nil替代。 AviatorEvaluator.execute("nil == nil"); //true AviatorEvaluator.execute(" 3> nil"); //true AviatorEvaluator.execute(" true!= nil"); //true AviatorEvaluator.execute(" ' '>nil "); //true AviatorEvaluator.execute(" a==nil "); //true,a is null nil與String相加的時候,跟java一樣顯示為null
(9) 日期比較
/** * CreateBy: haleyliu * CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { Map<String, Object> env = new HashMap<String, Object>(); final Date date = new Date(); String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(date); env.put("date", date); env.put("dateStr", dateStr); Boolean result = (Boolean) AviatorEvaluator.execute("date==dateStr", env); System.out.println(result); result = (Boolean) AviatorEvaluator.execute("date > '2009-12-20 00:00:00:00' ", env); System.out.println(result); result = (Boolean) AviatorEvaluator.execute("date < '2200-12-20 00:00:00:00' ", env); System.out.println(result); result = (Boolean) AviatorEvaluator.execute("date ==date ", env); System.out.println(result); } }
(10) 語法手冊
數據類型
-
Number類型:數字類型,支持兩種類型,分別對應Java的Long和Double,也就是說任何整數都將被轉換為Long,而任何浮點數都將被轉換為Double,包括用戶傳入的數值也是如此轉換。不支持科學計數法,僅支持十進制。如-1、100、2.3等。
-
String類型: 字符串類型,單引號或者雙引號括起來的文本串,如'hello world',變量如果傳入的是String或者Character也將轉為String類型。
-
Bool類型: 常量true和false,表示真值和假值,與java的Boolean.TRUE和Boolean.False對應。
-
Pattern類型: 類似Ruby、perl的正則表達式,以//括起來的字符串,如//d+/,內部實現為java.util.Pattern。
-
變量類型: 與Java的變量命名規則相同,變量的值由用戶傳入,如"a"、"b"等
-
nil類型: 常量nil,類似java中的null,但是nil比較特殊,nil不僅可以參與==、!=的比較,也可以參與>、>=、<、<=的比較,Aviator規定任何類型都n大於nil除了nil本身,nil==nil返回true。用戶傳入的變量值如果為null,那么也將作為nil處理,nil打印為null。
算術運算符
Aviator支持常見的算術運算符,包括+ - <tt></tt> / % 五個二元運算符,和一元運算符"-"。其中 - <tt></tt> / %和一元的"-"僅能作用於Number類型。
"+"不僅能用於Number類型,還可以用於String的相加,或者字符串與其他對象的相加。Aviator規定,任何類型與String相加,結果為String。
邏輯運算符
Avaitor的支持的邏輯運算符包括,一元否定運算符"!",以及邏輯與的"&&",邏輯或的"||"。邏輯運算符的操作數只能為Boolean。
關系運算符
Aviator支持的關系運算符包括"<" "<=" ">" ">=" 以及"=="和"!=" 。
&&和||都執行短路規則。
關系運算符可以作用於Number之間、String之間、Pattern之間、Boolean之間、變量之間以及其他類型與nil之間的關系比較,不同類型除了nil之外不能相互比較。
Aviator規定任何對象都比nil大除了nil之外。
匹配運算符
匹配運算符"=~"用於String和Pattern的匹配,它的左操作數必須為String,右操作數必須為Pattern。匹配成功后,Pattern的分組將存於變量$num,num為分組索引。
三元運算符
Aviator沒有提供if else語句,但是提供了三元運算符 "?:",形式為 bool ? exp1: exp2。 其中bool必須為結果為Boolean類型的表達式,而exp1和exp2可以為任何合法的Aviator表達式,並且不要求exp1和exp2返回的結果類型一致。
- 兩種模式
默認AviatorEvaluator以編譯速度優先:
AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE);
你可以修改為運行速度優先,這會做更多的編譯優化:
AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);
三.MVEL表達式解析器
1.簡介 :
MVEL在很大程度上受到Java語法的啟發,作為一個表達式語言,也有一些根本
的區別,旨在更高的效率,例如:直接支持集合、數組和字符串匹配等操作以
及正則表達式。 MVEL用於執行使用Java語法編寫的表達式。
2.特性:
MVEL是一個功能強大的基於Java應用程序的表達式語言。
目前最新的版本是2.0,具有以下特性: (1). 動態JIT優化器。當負載超過一個確保代碼產生的閾值時,選擇性地產生字 節代碼,這大大減少了內存的使用量。新的靜態類型檢查和屬性支持,允許集成 類型安全表達。 (2). 錯誤報告的改善。包括行和列的錯誤信息。 (3). 新的腳本語言特征。MVEL2.0 包含函數定義,如:閉包,lambda定義, 標准循環構造(for, while, do-while, do-until…),空值安全導航操作,內聯with -context運營 ,易變的(isdef)的測試運營等等。 (4). 改進的集成功能。迎合主流的需求,MVEL2.0支持基礎類型的個性化屬性處理器,集成到JIT中。 (5). 更快的模板引擎,支持線性模板定義,宏定義和個性化標記定義。 (6). 新的交互式shell(MVELSH)。 (7). 缺少可選類型安全 (8). 集成不良,通常通過映射填入內容。沒有字節碼不能運作用字節碼生成編 譯時間慢,還增加了可擴展性問題;不用字節碼生成運行時執行非常慢 (9). 內存消耗過大 (10). Jar巨大/依賴規模
3.原理:
與java不同,MVEL是動態類型(帶有可選分類),也就是說在源文件中是沒有
類型限制的。一條MVEL表達式,簡單的可以是單個標識符,復雜的則可能是
一個充滿了方法調用和內部集合創建的龐大的布爾表達式。
4.使用方式:
maven引入jar:
<dependency> <groupId>org.mvel</groupId> <artifactId>mvel2</artifactId> <version>2.3.1.Final</version> </dependency>
測試:
package com.lrq.wechatdemo.utils; import com.google.common.collect.Maps; import org.mvel2.MVEL; import java.util.Map; /** * CreateBy: haleyliu * CreateDate: 2018/12/26 */ public class MvelUtils { public static void main(String[] args) { String expression = "a == null && b == nil "; Map<String,Object> map = Maps.newHashMap(); map.put("a",null); map.put("b",null); Object object = MVEL.eval(expression,map); System.out.println(object); } }
四.EasyRules規則引擎
1.簡介:
easy-rules首先集成了mvel表達式,后續可能集成SpEL的一款輕量
級規則引擎
2.特性:
easy rules是一個簡單而強大的java規則引擎,它有以下特性:
輕量級框架,學習成本低
基於POJO
為定義業務引擎提供有用的抽象和簡便的應用
從原始的規則組合成復雜的規則
它主要包括幾個主要的類或接口:Rule,RulesEngine,RuleListener,Facts 還有幾個主要的注解:@Action,@Condition,@Fact,@Priority,@Rule
3.使用方式:
有兩種使用方式:
- java方式
首先先創建規則並標注屬性
package com.lrq.wechatdemo.rules; import org.jeasy.rules.annotation.Action; import org.jeasy.rules.annotation.Condition; import org.jeasy.rules.annotation.Fact; import org.jeasy.rules.annotation.Rule; import org.jeasy.rules.support.UnitRuleGroup; /** * CreateBy: haleyliu * CreateDate: 2018/12/26 */ public class RuleClass { @Rule(priority = 1) //規則設定優先級 public static class FizzRule { @Condition public boolean isFizz(@Fact("number") Integer number) { return number % 5 == 0; } @Action public void printFizz() { System.out.print("fizz\n"); } } @Rule(priority = 2) public static class BuzzRule { @Condition public boolean isBuzz(@Fact("number") Integer number) { return number % 7 == 0; } @Action public void printBuzz() { System.out.print("buzz\n"); } } public static class FizzBuzzRule extends UnitRuleGroup { public FizzBuzzRule(Object... rules) { for (Object rule : rules) { addRule(rule); } } @Override public int getPriority() { return 0; } } @Rule(priority = 3) public static class NonFizzBuzzRule { @Condition public boolean isNotFizzNorBuzz(@Fact("number") Integer number) { // can return true, because this is the latest rule to trigger according to // assigned priorities // and in which case, the number is not fizz nor buzz return number % 5 != 0 || number % 7 != 0; } @Action public void printInput(@Fact("number") Integer number) { System.out.print(number+"\n"); } } }
然后客戶端調用
package com.lrq.wechatdemo.rules; import org.jeasy.rules.api.Facts; import org.jeasy.rules.api.Rules; import org.jeasy.rules.api.RulesEngine; import org.jeasy.rules.core.DefaultRulesEngine; import org.jeasy.rules.core.RulesEngineParameters; /** * CreateBy: haleyliu * CreateDate: 2018/12/26 */ public class RuleJavaClient { public static void main(String[] args) { // 創建規則引擎 RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true); RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters); // 創建規則集並注冊規則 Rules rules = new Rules(); rules.register(new RuleClass.FizzRule()); rules.register(new RuleClass.BuzzRule()); rules.register(new RuleClass.FizzBuzzRule(new RuleClass.FizzRule(), new RuleClass.BuzzRule())); rules.register(new RuleClass.NonFizzBuzzRule()); // 執行規則 Facts facts = new Facts(); for (int i = 1; i <= 100; i++) { facts.put("number", i); fizzBuzzEngine.fire(rules, facts); System.out.println(); } } }
2.yml方式
resources目錄下新建fizzbuzz.yml
---
name: "fizz rule"
description: "print fizz if the number is multiple of 5"
priority: 1
condition: "number % 5 == 0"
actions:
- "System.out.println(\"fizz\")"
---
name: "buzz rule"
description: "print buzz if the number is multiple of 7"
priority: 2
condition: "number % 7 == 0"
actions:
- "System.out.println(\"buzz\")"
---
name: "fizzbuzz rule"
description: "print fizzbuzz if the number is multiple of 5 and 7"
priority: 0
condition: "number % 5 == 0 && number % 7 == 0"
actions:
- "System.out.println(\"fizzbuzz\")"
---
name: "non fizzbuzz rule"
description: "print the number itself otherwise"
priority: 3
condition: "number % 5 != 0 || number % 7 != 0"
actions:
- "System.out.println(number)"
客戶端調用:
package com.lrq.wechatdemo.rules; import org.jeasy.rules.api.Facts; import org.jeasy.rules.api.Rules; import org.jeasy.rules.api.RulesEngine; import org.jeasy.rules.core.DefaultRulesEngine; import org.jeasy.rules.core.RulesEngineParameters; import org.jeasy.rules.mvel.MVELRuleFactory; import java.io.FileNotFoundException; import java.io.FileReader; /** * CreateBy: haleyliu * CreateDate: 2018/12/26 */ public class RuleYmlClient { public static void main(String[] args) throws FileNotFoundException { // create a rules engine RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true); RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters); // create rules Rules rules = MVELRuleFactory.createRulesFrom(new FileReader("fizzbuzz.yml")); // fire rules Facts facts = new Facts(); for (int i = 1; i <= 100; i++) { facts.put("number", i); fizzBuzzEngine.fire(rules, facts); System.out.println(); } } }
http://www.findsrc.com/java/detail/8664
Java表達式引擎fel/groovy/expression4j/java腳本引擎的性能對比【原創】
發布時間:2016-06-07 10:48:10
有 7221 人瀏覽來源:碼雲網又是性能對比,最近跟性能較上勁了。
產品中需要用到數學表達式,表達式不復雜,但是對性能要求比較高。選用了一些常用的表達式引擎計算方案,包含:java腳本引擎(javax/script)、groovy腳本引擎、Expression4j、Fel表達式引擎。
其中java腳本引擎使用了解釋執行和編譯執行兩種方式、groovy腳本只采用了編譯執行(解釋執行太慢)、Fel采用了靜態參數和動態參數兩種方式。以下為測試代碼:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
public
class
ExpressionTest
extends
BaseTest {
private
int
count
=
100000
;
//javax的編譯執行,效率比解釋執行略高?為什么才略高??
@Test
public
void
testCompiledJsScript()
throws
Throwable {
javax.script.ScriptEngine se =
new
ScriptEngineManager().getEngineByName(
"js"
);
Compilable ce = (Compilable) se;
CompiledScript cs = ce.compile(
"a*b*c"
);
Bindings bindings = se.createBindings();
bindings.put(
"a"
,
3600
);
bindings.put(
"b"
,
14
);
bindings.put(
"c"
,
4
);
long
start = System.currentTimeMillis();
for (
int
i =
0
; i <
count
; i++) {
cs.eval(bindings);
}
System.out.
println
(System.currentTimeMillis() - start);
}
//javax script解釋執行
@Test
public
void
testJsScript()
throws
Throwable {
javax.script.ScriptEngine se =
new
ScriptEngineManager().getEngineByName(
"js"
);
Bindings bindings = se.createBindings();
bindings.put(
"a"
,
3600
);
bindings.put(
"b"
,
14
);
bindings.put(
"c"
,
4
);
long
start = System.currentTimeMillis();
for (
int
i =
0
; i <
count
; i++) {
se.eval(
"a*b*c"
, bindings);
}
System.out.
println
(System.currentTimeMillis() - start);
}
//groovy的編譯執行
@Test
public
void
testGroovy() {
//這里的ScriptEngine和GroovyScriptEngine是自己編寫的類,不是原生的
ScriptEngine se = this.getBean(GroovyScriptEngine.
class
);
Map<String, Object> paramMap =
new
HashMap<String, Object>();
paramMap.put(
"param"
,
5
);
//ScriptEngine首次執行會緩存編譯后的腳本,這里故意先執行一次便於緩存
se.eval(
"3600*34*param"
, paramMap);
long
start = System.currentTimeMillis();
for (
int
i =
0
; i <
count
; i++) {
se.eval(
"3600*34*param"
, paramMap);
}
System.out.
println
(System.currentTimeMillis() - start);
}
//Expression4J的表達式引擎,這里是通過函數的方式,有點特別
@Test
public
void
testExpression4j()
throws
Throwable {
Expression expression = ExpressionFactory.createExpression(
"f(a,b,c)=a*b*c"
);
System.out.
println
(
"Expression name: "
+ expression.getName());
System.out.
println
(
"Expression parameters: "
+ expression.getParameters());
MathematicalElement element_a = NumberFactory.createReal(
3600
);
MathematicalElement element_b = NumberFactory.createReal(
34
);
MathematicalElement element_c = NumberFactory.createReal(
5
);
Parameters parameters = ExpressionFactory.createParameters();
parameters.addParameter(
"a"
, element_a);
parameters.addParameter(
"b"
, element_b);
parameters.addParameter(
"c"
, element_c);
long
start = System.currentTimeMillis();
for (
int
i =
0
; i <
count
; i++) {
expression.evaluate(parameters);
}
System.out.
println
(System.currentTimeMillis() - start);
}
//fel的表達式引擎(靜態參數,同上面)
@Test
public
void
felTest() {
FelEngine e = FelEngine.instance;
final FelContext ctx = e.getContext();
ctx.set(
"a"
,
3600
);
ctx.set(
"b"
,
14
);
ctx.set(
"c"
,
5
);
com.greenpineyu.fel.Expression exp = e.compile(
"a*b*c"
, ctx);
long
start = System.currentTimeMillis();
Object eval =
null
;
for (
int
i =
0
; i <
count
; i++) {
eval = exp.eval(ctx);
}
System.out.
println
(System.currentTimeMillis() - start);
System.out.
println
(eval);
}
//fel表達式引擎(動態參數,這里動態參數的產生和變量改變都會消耗時間,因此這個測試時間不准確,只是驗證對於動態參數的支持)
@Test
public
void
felDynaTest() {
FelEngine e = FelEngine.instance;
final FelContext ctx = e.getContext();
ctx.set(
"a"
,
3600
);
ctx.set(
"b"
,
14
);
ctx.set(
"c"
,
5
);
com.greenpineyu.fel.Expression exp = e.compile(
"a*b*c"
, ctx);
long
start = System.currentTimeMillis();
Object eval =
null
;
Random r =
new
Random();
for (
int
i =
0
; i <
count
; i++) {
ctx.set(
"a"
, r.nextInt(
10000
));
ctx.set(
"b"
, r.nextInt(
100
));
ctx.set(
"c"
, r.nextInt(
100
));
eval = exp.eval(ctx);
}
System.out.
println
(System.currentTimeMillis() - start);
System.out.
println
(eval);
}
public
static
void
main(String[] args)
throws
Throwable {
ExpressionTest et =
new
ExpressionTest();
//執行100W次的測試
et.
count
=
1000000
;
et.testCompiledJsScript();
et.testJsScript();
et.testExpression4j();
et.testGroovy();
et.felTest();
}
}
|
測試結果如下:
| 表達式引擎 | 執行時間(毫秒) | 備注 |
| java腳本引擎編譯后執行 |
7662 |
|
| java腳本引擎解釋執行 | 10609 | |
| expression4j | 578 | |
| groovy編譯執行 | 224 | |
| fel靜態參數 | 19 | |
| fel動態參數 | 107 | 該項測試比較不公平,隨機數的產生以及參數的變更也會占用一定時間,測試目的只是為了驗證是不是存在靜態優化,從而導致靜態性能遠高於動態性能的情況。 |
結論:
從以上性能對比來看(拋開表達式的功能),fel明顯占據很大優勢,groovy和expression4j也是可以接受的。java腳本引擎的執行偏慢。因此,對於表達式不是很復雜性能要求高的情況下,推薦使用fel或者groovy編譯執行的方式。





