一. Drools規(guī)則引擎
- 簡介:
Drools就是為了解決業(yè)務代碼和業(yè)務規(guī)則分離的引擎。
Drools 規(guī)則是在 Java 應用程序上運行的,其要執(zhí)行的步驟順序由代碼確定
,為了實現(xiàn)這一點,Drools 規(guī)則引擎將業(yè)務規(guī)則轉換成執(zhí)行樹。
- 特性:
優(yōu)點:
1、簡化系統(tǒng)架構,優(yōu)化應用
2、提高系統(tǒng)的可維護性和維護成本
3、方便系統(tǒng)的整合
4、減少編寫“硬代碼”業(yè)務規(guī)則的成本和風險
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規(guī)則文件/src/resources/rules/age.drl
import com.lrq.wechatDemo.domain.User // 導入類
dialect "mvel"
rule "age" // 規(guī)則名,唯一
when
$user : User(age<15 || age>60) //規(guī)則的條件部分
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語言實現(xiàn)的表達式求值引擎,主要用于各
種表達式的動態(tài)求值?,F(xiàn)在已經(jīng)有很多開源可用的java表達式求值引擎,為什
么還需要Avaitor呢?
Aviator的設計目標是輕量級和高性能 ,相比于Groovy、JRuby的笨重,Aviator
非常小,加上依賴包也才450K,不算依賴包的話只有70K;當然,Aviator的語法
是受限的,它不是一門完整的語言,而只是語言的一小部分集合。
其次,Aviator的實現(xiàn)思路與其他輕量級的求值器很不相同,其他求值器一般都
是通過解釋的方式運行,而Aviator則是直接將表達式編譯成Java字節(jié)碼,交給
JVM去執(zhí)行。簡單來說,Aviator的定位是介于Groovy這樣的重量級腳本語言和
IKExpression這樣的輕量級表達式引擎之間。
- 特性:
(1)支持大部分運算操作符,包括算術操作符、關系運算符、邏輯操作符、
正則匹配操作符(=~)、三元表達式?: ,并且支持操作符的優(yōu)先級和括號強制優(yōu)
先級,具體請看后面的操作符列表。
(2)支持函數(shù)調用和自定義函數(shù)。
(3)支持正則表達式匹配,類似Ruby、Perl的匹配語法,并且支持類Ruby的
$digit指向匹配分組。自動類型轉換,當執(zhí)行操作的時候,會自動判斷操作數(shù)類
型并做相應轉換,無法轉換即拋異常。
(4)支持傳入變量,支持類似a.b.c的嵌套變量訪問。
(5)性能優(yōu)秀。
(6)Aviator的限制,沒有if else、do while等語句,沒有賦值語句,僅支持邏
輯表達式、算術表達式、三元表達式和正則匹配。沒有位運算符
-
整體結構:
整體結構.png maven依賴:
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>${aviator.version}</version>
</dependency>
- 執(zhí)行方式
執(zhí)行表達式的方法有兩個:execute()、exec();
execute(),需要傳遞Map格式參數(shù)
exec(),不需要傳遞Map
示例:
/**
* CreateBy: haleyliu
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
// exec執(zhí)行方式,無需傳遞Map格式
String age = "18";
System.out.println(AviatorEvaluator.exec("'His age is '+ age +'!'", age));
// execute執(zhí)行方式,需傳遞Map格式
Map<String, Object> map = new HashMap<String, Object>();
map.put("age", "18");
System.out.println(AviatorEvaluator.execute("'His age is '+ age +'!'",
map));
}
}
-
使用函數(shù)
Aviator可以使用兩種函數(shù):內置函數(shù)、自定義函數(shù)
(1)內置函數(shù)
Aviator內置函數(shù).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)自定義函數(shù)
自定義函數(shù)要繼承AbstractFunction類,重寫目標方法。
/**
* CreateBy: haleyliu
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
// 注冊自定義函數(shù)
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);
// 執(zhí)行表達式
Boolean result = (Boolean) compiledExp.execute(env);
System.out.println(result);
}
}
(4) 訪問數(shù)組和集合
List和數(shù)組用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規(guī)定,[任何對象都比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) 語法手冊
數(shù)據(jù)類型
Number類型:數(shù)字類型,支持兩種類型,分別對應Java的Long和Double,也就是說任何整數(shù)都將被轉換為Long,而任何浮點數(shù)都將被轉換為Double,包括用戶傳入的數(shù)值也是如此轉換。不支持科學計數(shù)法,僅支持十進制。如-1、100、2.3等。
String類型: 字符串類型,單引號或者雙引號括起來的文本串,如'hello world',變量如果傳入的是String或者Character也將轉為String類型。
Bool類型: 常量true和false,表示真值和假值,與java的Boolean.TRUE和Boolean.False對應。
Pattern類型: 類似Ruby、perl的正則表達式,以//括起來的字符串,如//d+/,內部實現(xiàn)為java.util.Pattern。
變量類型: 與Java的變量命名規(guī)則相同,變量的值由用戶傳入,如"a"、"b"等
nil類型: 常量nil,類似java中的null,但是nil比較特殊,nil不僅可以參與==、!=的比較,也可以參與>、>=、<、<=的比較,Aviator規(guī)定任何類型都n大于nil除了nil本身,nil==nil返回true。用戶傳入的變量值如果為null,那么也將作為nil處理,nil打印為null。
算術運算符
Aviator支持常見的算術運算符,包括+ - <tt></tt> / % 五個二元運算符,和一元運算符"-"。其中 - <tt></tt> / %和一元的"-"僅能作用于Number類型。
"+"不僅能用于Number類型,還可以用于String的相加,或者字符串與其他對象的相加。Aviator規(guī)定,任何類型與String相加,結果為String。
邏輯運算符
Avaitor的支持的邏輯運算符包括,一元否定運算符"!",以及邏輯與的"&&",邏輯或的"||"。邏輯運算符的操作數(shù)只能為Boolean。
關系運算符
Aviator支持的關系運算符包括"<" "<=" ">" ">=" 以及"=="和"!=" 。
&&和||都執(zhí)行短路規(guī)則。
關系運算符可以作用于Number之間、String之間、Pattern之間、Boolean之間、變量之間以及其他類型與nil之間的關系比較,不同類型除了nil之外不能相互比較。
Aviator規(guī)定任何對象都比nil大除了nil之外。
匹配運算符
匹配運算符"=~"用于String和Pattern的匹配,它的左操作數(shù)必須為String,右操作數(shù)必須為Pattern。匹配成功后,Pattern的分組將存于變量$num,num為分組索引。
三元運算符
Aviator沒有提供if else語句,但是提供了三元運算符 "?:",形式為 bool ? exp1: exp2。 其中bool必須為結果為Boolean類型的表達式,而exp1和exp2可以為任何合法的Aviator表達式,并且不要求exp1和exp2返回的結果類型一致。
- 兩種模式
默認AviatorEvaluator以編譯速度優(yōu)先:
AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE);
你可以修改為運行速度優(yōu)先,這會做更多的編譯優(yōu)化:
AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);
三.MVEL表達式解析器
1.簡介 :
MVEL在很大程度上受到Java語法的啟發(fā),作為一個表達式語言,也有一些根本
的區(qū)別,旨在更高的效率,例如:直接支持集合、數(shù)組和字符串匹配等操作以
及正則表達式。 MVEL用于執(zhí)行使用Java語法編寫的表達式。
2.特性:
MVEL是一個功能強大的基于Java應用程序的表達式語言。
目前最新的版本是2.0,具有以下特性:
(1). 動態(tài)JIT優(yōu)化器。當負載超過一個確保代碼產(chǎn)生的閾值時,選擇性地產(chǎn)生字
節(jié)代碼,這大大減少了內存的使用量。新的靜態(tài)類型檢查和屬性支持,允許集成
類型安全表達。
(2). 錯誤報告的改善。包括行和列的錯誤信息。
(3). 新的腳本語言特征。MVEL2.0 包含函數(shù)定義,如:閉包,lambda定義,
標準循環(huán)構造(for, while, do-while, do-until…),空值安全導航操作,內聯(lián)with
-context運營 ,易變的(isdef)的測試運營等等。
(4). 改進的集成功能。迎合主流的需求,MVEL2.0支持基礎類型的個性化屬性處理器,集成到JIT中。
(5). 更快的模板引擎,支持線性模板定義,宏定義和個性化標記定義。
(6). 新的交互式shell(MVELSH)。
(7). 缺少可選類型安全
(8). 集成不良,通常通過映射填入內容。沒有字節(jié)碼不能運作用字節(jié)碼生成編
譯時間慢,還增加了可擴展性問題;不用字節(jié)碼生成運行時執(zhí)行非常慢
(9). 內存消耗過大
(10). Jar巨大/依賴規(guī)模
3.原理:
與java不同,MVEL是動態(tài)類型(帶有可選分類),也就是說在源文件中是沒有
類型限制的。一條MVEL表達式,簡單的可以是單個標識符,復雜的則可能是
一個充滿了方法調用和內部集合創(chuàng)建的龐大的布爾表達式。
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規(guī)則引擎
1.簡介:
easy-rules首先集成了mvel表達式,后續(xù)可能集成SpEL的一款輕量
級規(guī)則引擎
2.特性:
easy rules是一個簡單而強大的java規(guī)則引擎,它有以下特性:
輕量級框架,學習成本低
基于POJO
為定義業(yè)務引擎提供有用的抽象和簡便的應用
從原始的規(guī)則組合成復雜的規(guī)則
它主要包括幾個主要的類或接口:Rule,RulesEngine,RuleListener,Facts
還有幾個主要的注解:@Action,@Condition,@Fact,@Priority,@Rule
3.使用方式:
@Rule可以標注name和description屬性,每個rule的name要唯一,
如果沒有指定,則RuleProxy則默認取類名
@Condition是條件判斷,要求返回boolean值,表示是否滿足條件
@Action標注條件成立之后觸發(fā)的方法
@Priority標注該rule的優(yōu)先級,默認是Integer.MAX_VALUE - 1,值
越小越優(yōu)先
@Fact 我們要注意Facts的使用。Facts的用法很像Map,它是客戶
端和規(guī)則文件之間通信的橋梁。在客戶端使用put方法向Facts中添
加數(shù)據(jù),在規(guī)則文件中通過key來得到相應的數(shù)據(jù)。
有兩種使用方式:
- java方式
首先先創(chuàng)建規(guī)則并標注屬性
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) //規(guī)則設定優(yōu)先級
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) {
// 創(chuàng)建規(guī)則引擎
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);
// 創(chuàng)建規(guī)則集并注冊規(guī)則
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());
// 執(zhí)行規(guī)則
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();
}
}
}


