1.規則引擎概述
1.1 什么是規則引擎
規則引擎 全稱為業務規則管理系統,英文名為BRMS,規則引擎的主要思想是將應用程序中的業務決策部分分離出來,并使用預定義的語義模塊編寫業務規則,由用戶或開發者在需要時進行配置和管理。
需要注意的是規則引擎并不是一個具體的技術框架,而是指的一類系統,即業務規則管理系統。目前市面上具體的規則引擎產品有:drools、VisualRules、iLog等。
規則引擎實現了將業務決策從應用程序代碼中分離出來,接收數據輸入,解釋業務規則,并根據業務規則做出業務決策。規則引擎其實就是一個輸入輸出平臺。
1.2 使用規則引擎的優勢
- 業務規則與系統代碼分離,實現業務規則的集中管理
- 在不重啟服務的情況下隨時對業務規則進行擴展和維護
- 可以動態修改業務規則, 從而快速響應需求變更
- 規則引擎是相對獨立的
- 減少了硬編碼業務規則的成本和風險
- 使用規則引擎提供的規則編輯工具,使復雜的業務規則變得簡單
1.3 規則引擎應用場景
- 風險控制系統----風險貸款、風險評估
- 反欺詐項目----銀行貸款、征信驗證
- 決策平臺系統----財務計算
- 促銷平臺系統----滿減、打折、加價購
1.4 Drools 介紹
drools 是一款由JBoss組織提供的基于Java語言開發的開源規則引擎,可以將復雜且多變的業務規則從硬編碼中解放出來,以規則腳本的形式存放在文件或特定的存儲介質中(例如存放在數據庫中),使得業務規則的變更不需要修改項目代碼、重啟服務器就可以在線上環境立即生效。
drools官網地址:https://drools.org/
drools源碼下載地址:https://github.com/kiegroup/drools
在項目中使用drools時,即可以單獨使用也可以整合spring使用。如果單獨使用只需要導入如下maven坐標即可:
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.6.0.Final</version>
</dependency>
如果我們使用IDEA開發drools應用,IDEA中已經集成了drools插件。如果使用eclipse開發drools應用還需要單獨安裝drools插件。
drools API開發步驟如下:
2. Drools 入門案例
首先通過一個Drools入門案例,初步了解Drools的使用方法,對Drools有一個整體概念。
2.1 業務場景說明
業務場景:消費者在圖書商城購買圖書,下單后需要在支付頁面顯示訂單優惠后的價格。具體優惠規則如下:
規則編號 | 規則名稱 | 描述 |
---|---|---|
1 | 規則一 | 所購圖書總價在100元的沒有優惠 |
2 | 規則二 | 所購圖書總價在100元到200元的優惠20元 |
3 | 規則三 | 所購圖書總價在200到300元的優惠50元 |
4 | 規則四 | 所購圖書總價在300元以上的優惠100元 |
現在需要根據上面的規則計算優惠后的價格。 |
3.2 開發實現
第一步:創建maven工程drools_quickstart并導入drools相關maven坐標
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.10.0.Final</version>
</dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency>
第二步:根據drools要求創建resources/META-INF/kmodule.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule"><!--name:指定kbase的名稱,可以任意,但是需要唯一packages:指定規則文件的目錄,需要根據實際情況填寫,否則無法加載到規則文件default:指定當前kbase是否為默認--><kbase name="myKbase1" packages="rules" default="true"><!--name:指定ksession名稱,可以任意,但是需要唯一default:指定當前session是否為默認--><ksession name="ksession-rule" default="true"/></kbase>
</kmodule>
注意:上面配置文件的名稱和位置都是固定寫法,不能更改
第三步:創建實體類Order
package com.itheima.drools.entity;/*** 訂單*/
public class Order {private Double originalPrice;//訂單原始價格,即優惠前價格private Double realPrice;//訂單真實價格,即優惠后價格public String toString() {return "Order{" +"originalPrice=" + originalPrice +", realPrice=" + realPrice +'}';}public Double getOriginalPrice() {return originalPrice;}public void setOriginalPrice(Double originalPrice) {this.originalPrice = originalPrice;}public Double getRealPrice() {return realPrice;}public void setRealPrice(Double realPrice) {this.realPrice = realPrice;}
}
第四步:創建規則文件resources/rules/bookDiscount.drl
//圖書優惠規則
package book.discount
import com.itheima.drools.entity.Order//規則一:所購圖書總價在100元以下的沒有優惠
rule "book_discount_1"when$order:Order(originalPrice < 100)then$order.setRealPrice($order.getOriginalPrice());System.out.println("成功匹配到規則一:所購圖書總價在100元以下的沒有優惠");
end//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"when$order:Order(originalPrice < 200 && originalPrice >= 100)then$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end//規則三:所購圖書總價在200到300元的優惠50元
rule "book_discount_3"when$order:Order(originalPrice <= 300 && originalPrice >= 200)then$order.setRealPrice($order.getOriginalPrice() - 50);System.out.println("成功匹配到規則三:所購圖書總價在200到300元的優惠50元");
end//規則四:所購圖書總價在300元以上的優惠100元
rule "book_discount_4"when$order:Order(originalPrice >= 300)then$order.setRealPrice($order.getOriginalPrice() - 100);System.out.println("成功匹配到規則四:所購圖書總價在300元以上的優惠100元");
end
第五步:編寫單元測試
@Test
public void test1(){KieServices kieServices = KieServices.Factory.get();KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();//會話對象,用于和規則引擎交互KieSession kieSession = kieClasspathContainer.newKieSession();//構造訂單對象,設置原始價格,由規則引擎根據優惠規則計算優惠后的價格Order order = new Order();order.setOriginalPrice(210D);//將數據提供給規則引擎,規則引擎會根據提供的數據進行規則匹配kieSession.insert(order);//激活規則引擎,如果規則匹配成功則執行規則kieSession.fireAllRules();//關閉會話kieSession.dispose();System.out.println("優惠前原始價格:" + order.getOriginalPrice() +",優惠后價格:" + order.getRealPrice());
}
通過上面的入門案例我們可以發現,使用drools規則引擎主要工作就是編寫規則文件,在規則文件中定義跟業務相關的業務規則,例如本案例定義的就是圖書優惠規則。規則定義好后就需要調用drools提供的API將數據提供給規則引擎進行規則模式匹配,規則引擎會執行匹配成功的規則并將計算的結果返回給我們。
可能大家會有疑問,就是我們雖然沒有在代碼中編寫規則的判斷邏輯,但是我們還是在規則文件中編寫了業務規則,這跟在代碼中編寫規則有什么本質的區別呢?
我們前面其實已經提到,使用規則引擎時業務規則可以做到動態管理。業務人員可以像管理數據一樣對業務規則進行管理,比如查詢、添加、更新、統計、提交業務規則等。這樣就可以做到在不重啟服務的情況下調整業務規則。
3.3 小結
3.3.1 規則引擎構成
drools規則引擎由以下三部分構成:
- Working Memory(工作內存)
- Rule Base(規則庫)
- Inference Engine(推理引擎)
其中Inference Engine(推理引擎)又包括:
- Pattern Matcher(匹配器)
- Agenda(議程)
- Execution Engine(執行引擎)
如下圖所示:
3.3.2 相關概念說明
Working Memory:工作內存,drools規則引擎會從Working Memory中獲取數據并和規則文件中定義的規則進行模式匹配,所以我們開發的應用程序只需要將我們的數據插入到Working Memory中即可,例如本案例中我們調用kieSession.insert(order)就是將order對象插入到了工作內存中。
Fact:事實,是指在drools 規則應用當中,將一個普通的JavaBean插入到Working Memory后的對象就是Fact對象,例如本案例中的Order對象就屬于Fact對象。Fact對象是我們的應用和規則引擎進行數據交互的橋梁或通道。
Rule Base:規則庫,我們在規則文件中定義的規則都會被加載到規則庫中。
Pattern Matcher:匹配器,將Rule Base中的所有規則與Working Memory中的Fact對象進行模式匹配,匹配成功的規則將被激活并放入Agenda中。
Agenda:議程,用于存放通過匹配器進行模式匹配后被激活的規則。
Execution Engine:執行引擎,執行Agenda中被激活的規則。
3.3.3 規則引擎執行過程
3.3.4 KIE介紹
我們在操作Drools時經常使用的API以及它們之間的關系如下圖:
通過上面的核心API可以發現,大部分類名都是以Kie開頭。Kie全稱為Knowledge Is Everything,即"知識就是一切"的縮寫,是Jboss一系列項目的總稱。如下圖所示,Kie的主要模塊有OptaPlanner、Drools、UberFire、jBPM。
通過上圖可以看到,Drools是整個KIE項目中的一個組件,Drools中還包括一個Drools-WB的模塊,它是一個可視化的規則編輯器。
3. Drools基礎語法
3.1 規則文件構成
在使用Drools時非常重要的一個工作是編寫規則文件,通常規則文件的后綴為drl
drl 的Drools Rule Language 的縮寫,在規則文件中編寫內容的規則如下:
關鍵字 | 描述 |
---|---|
package | 包名,只限于邏輯上的管理,同一個包名下的查詢或者函數可以直接調用 |
import | 用于導入類或者靜態方法 |
global | 全局變量 |
function | 自定義函數 |
query | 查詢 |
rule end | 規則體 |
Drools支持的規則文件,除了drl形式,還有Excel文件類型的。
3.2 規則體語法結構
規則體是規則文件內容中的重要組成部分,是進行業務規則判斷、處理業務結果的部分。
規則體語法結構如下:
rule "ruleName"attributeswhenLHSthenRHS
end
rule:關鍵字,表示規則開始,參數為規則的唯一名稱。
attribute:規則屬性, 是rule與when之間的參數,為可選項
when :關鍵字,后面跟規則的條件部分
LHS:是規則的條件部分的通用名稱。它由零個或多個條件元素組成。如果LHS為空,則它將被視為始終為true的條件元素。
then:關鍵字,后面跟規則的結果部分。
RHS(Right Hand Side):是規則的后果或行動部分的通用名稱。
end:關鍵字,表示一個規則結束。
3.3 注釋
在drl形式的規則文件中使用注釋和Java類中使用注釋一致,分為單行注釋和多行注釋。
單行注釋用"//“進行標記,多行注釋以”/*“開始,以”*/"結束。如下示例:
//規則rule1的注釋,這是一個單行注釋
rule "rule1"whenthenSystem.out.println("rule1觸發");
end/*
規則rule2的注釋,
這是一個多行注釋
*/
rule "rule2"whenthenSystem.out.println("rule2觸發");
end
3.4 Pattern模式匹配
前面我們已經知道了Drools中的匹配器可以將Rule Base中的所有規則與Working Memory中的Fact對象進行模式匹配,那么我們就需要在規則體的LHS部分定義規則并進行模式匹配。LHS部分由一個或者多個條件組成,條件又稱為pattern。
pattern的語法結構為:綁定變量名:Object(Field約束)
其中綁定變量名可以省略,通常綁定變量名的命名一般建議以$開始。如果定義了綁定變量名,就可以在規則體的RHS部分使用此綁定變量名來操作相應的Fact對象。Field約束部分是需要返回true或者false的0個或多個表達式。
例如我們的入門案例中:
//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"when//Order為類型約束,originalPrice為屬性約束$order:Order(originalPrice < 200 && originalPrice >= 100)then$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end
通過上面的例子我們可以知道,匹配的條件為:
1、工作內存中必須存在Order這種類型的Fact對象-----類型約束
2、Fact對象的originalPrice屬性值必須小于200------屬性約束
3、Fact對象的originalPrice屬性值必須大于等于100------屬性約束
以上條件必須同時滿足當前規則才有可能被激活。
綁定變量既可以用在對象上,也可以用在對象的屬性上。例如上面的例子可以改為:
//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"when$order:Order($op:originalPrice < 200 && originalPrice >= 100)thenSystem.out.println("$op=" + $op);$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end
LHS部分還可以定義多個pattern,多個pattern之間可以使用and或者or進行連接,也可以不寫,默認連接為and。
//規則二:所購圖書總價在100到200元的優惠20元
rule "book_discount_2"when$order:Order($op:originalPrice < 200 && originalPrice >= 100) and$customer:Customer(age > 20 && gender=='male')thenSystem.out.println("$op=" + $op);$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("成功匹配到規則二:所購圖書總價在100到200元的優惠20元");
end
3.5 比較操作符
Drools提供的比較操作符,如下表:
符號 | 說明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
contains | 檢查一個Fact對象的某個屬性值是否包含一個指定的對象值 |
not contains | 檢查一個Fact對象的某個屬性值是否不包含一個指定的對象值 |
memberOf | 判斷一個Fact對象的某個屬性是否在一個或多個集合中 |
not memberOf | 判斷一個Fact對象的某個屬性是否不在一個或多個集合中 |
matches | 判斷一個Fact對象的屬性是否與提供的標準的Java正則表達式進行匹配 |
not matches | 判斷一個Fact對象的屬性是否不與提供的標準的Java正則表達式進行匹配 |
前6個比較操作符和Java中的完全相同,下面我們重點學習后6個比較操作符。
3.5.1 語法
-
contains | not contains語法結構
Object(Field[Collection/Array] contains value)
Object(Field[Collection/Array] not contains value)
-
memberOf | not memberOf語法結構
Object(field memberOf value[Collection/Array])
Object(field not memberOf value[Collection/Array])
-
matches | not matches語法結構
Object(field matches “正則表達式”)
Object(field not matches “正則表達式”)
3.5.2 操作步驟
第一步:創建實體類,用于測試比較操作符
package com.itheima.drools.entity;
import java.util.List;/*** 實體類* 用于測試比較操作符*/
public class ComparisonOperatorEntity {private String names;private List<String> list;public String getNames() {return names;}public void setNames(String names) {this.names = names;}public List<String> getList() {return list;}public void setList(List<String> list) {this.list = list;}
}
第二步:在/resources/rules下創建規則文件comparisonOperator.drl
package comparisonOperator
import com.itheima.drools.entity.ComparisonOperatorEntity
/*當前規則文件用于測試Drools提供的比較操作符
*///測試比較操作符contains
rule "rule_comparison_contains"whenComparisonOperatorEntity(names contains "張三")ComparisonOperatorEntity(list contains names)thenSystem.out.println("規則rule_comparison_contains觸發");
end//測試比較操作符not contains
rule "rule_comparison_notContains"whenComparisonOperatorEntity(names not contains "張三")ComparisonOperatorEntity(list not contains names)thenSystem.out.println("規則rule_comparison_notContains觸發");
end//測試比較操作符memberOf
rule "rule_comparison_memberOf"whenComparisonOperatorEntity(names memberOf list)thenSystem.out.println("規則rule_comparison_memberOf觸發");
end//測試比較操作符not memberOf
rule "rule_comparison_notMemberOf"whenComparisonOperatorEntity(names not memberOf list)thenSystem.out.println("規則rule_comparison_notMemberOf觸發");
end//測試比較操作符matches
rule "rule_comparison_matches"whenComparisonOperatorEntity(names matches "張.*")thenSystem.out.println("規則rule_comparison_matches觸發");
end//測試比較操作符not matches
rule "rule_comparison_notMatches"whenComparisonOperatorEntity(names not matches "張.*")thenSystem.out.println("規則rule_comparison_notMatches觸發");
end
第三步:編寫單元測試
//測試比較操作符@Testpublic void test2() {KieServices kieServices = KieServices.Factory.get();KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();KieSession kieSession = kieClasspathContainer.newKieSession();ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();comparisonOperatorEntity.setNames("張三");List<String> list = new ArrayList<>();list.add("張三");list.add("李四");comparisonOperatorEntity.setList(list);//將數據提供給規則引擎,規則引擎會根據提供的數據進行規則匹配,如果規則匹配成功則執行規則kieSession.insert(comparisonOperatorEntity);/* //激活全部規則kieSession.fireAllRules();*///通過規則過濾器實現只執行指定規則kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule_comparison_contains"));kieSession.dispose();}
3.6 Drools內置方法
規則文件的RHS
部分的主要作用是通過插入,刪除或修改工作內存中的Fact數據,來達到控制規則引擎執行的目的。Drools提供了一些方法可以用來操作工作內存中的數據,操作完成后規則引擎會重新進行相關規則的匹配,原來沒有匹配成功的規則在我們修改數據完成后有可能就會匹配成功了。
3.6.1 update方法
update方法的作用是更新工作內存中的數據,并讓相關的規則重新匹配。
第一步:編寫規則文件/resources/rules/student.drl,文件內容如下
package student
import com.itheima.drools.entity.Student/*當前規則文件用于測試Drools提供的內置方法
*/rule "rule_student_age小于10歲"when$s:Student(age < 10)then$s.setAge(15);update($s);//更新數據,導致相關的規則會重新匹配System.out.println("規則rule_student_age小于10歲觸發");
endrule "rule_student_age小于20歲同時大于10歲"when$s:Student(age < 20 && age > 10)then$s.setAge(25);update($s);//更新數據,導致相關的規則會重新匹配System.out.println("規則rule_student_age小于20歲同時大于10歲觸發");
endrule "rule_student_age大于20歲"when$s:Student(age > 20)thenSystem.out.println("規則rule_student_age大于20歲觸發");
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();Student student = new Student();
student.setAge(5);//將數據提供給規則引擎,規則引擎會根據提供的數據進行規則匹配,如果規則匹配成功則執行規則
kieSession.insert(student);kieSession.fireAllRules();
kieSession.dispose();
通過控制臺的輸出可以看到規則文件中定義的三個規則都觸發了。
3.6.2 insert方法
insert方法作用是向工作內容中插入數據,并讓相關規則重新匹配
直接修改student.drl即可
rule "rule_student_age等于10歲"when$s:Student(age == 10)thenStudent student = new Student();student.setAge(5);insert(student);//插入數據,導致相關的規則會重新匹配System.out.println("規則rule_student_age等于10歲觸發");
end
3.6.3 retract方法
retract方法的作用是刪除工作內存中的數據,并讓相關的規則重新匹配。
第一步:修改student.drl文件內容如下
package student
import com.itheima.drools.entity.Student/*當前規則文件用于測試Drools提供的內置方法
*/rule "rule_student_age等于10歲時刪除數據"/*salience:設置當前規則的執行優先級,數值越大越優先執行,默認值為0.因為當前規則的匹配條件和下面規則的匹配條件相同,為了保證先執行當前規則,需要設置優先級*/salience 100 when$s:Student(age == 10)thenretract($s);//retract方法的作用是刪除工作內存中的數據,并讓相關的規則重新匹配。System.out.println("規則rule_student_age等于10歲時刪除數據觸發");
endrule "rule_student_age等于10歲"when$s:Student(age == 10)thenStudent student = new Student();student.setAge(5);insert(student);System.out.println("規則rule_student_age等于10歲觸發");
endrule "rule_student_age小于10歲"when$s:Student(age < 10)then$s.setAge(15);update($s);System.out.println("規則rule_student_age小于10歲觸發");
endrule "rule_student_age小于20歲同時大于10歲"when$s:Student(age < 20 && age > 10)then$s.setAge(25);update($s);System.out.println("規則rule_student_age小于20歲同時大于10歲觸發");
endrule "rule_student_age大于20歲"when$s:Student(age > 20)thenSystem.out.println("規則rule_student_age大于20歲觸發");
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();Student student = new Student();
student.setAge(10);//將數據提供給規則引擎,規則引擎會根據提供的數據進行規則匹配,如果規則匹配成功則執行規則
kieSession.insert(student);kieSession.fireAllRules();
kieSession.dispose();
通過控制臺輸出可以發現,只有第一個規則觸發了,因為在第一個規則中將工作內存中的數據刪除了導致第二個規則并沒有匹配成功。
4.規則屬性
規則體的構成如下:
rule "ruleName"attributeswhenLHSthenRHS
end
Drools中提供的屬性如下表(部分屬性)):
屬性名 | 說明 |
---|---|
salience | 指定規則執行優先級 |
dialect | 指定規則使用的語言類型,取值為java和mvel |
enabled | 指定規則是否啟用 |
date-effective | 指定規則生效時間 |
date-expires | 指定規則失效時間 |
activation-group | 激活分組,具有相同分組名稱的規則只能有一個規則觸發 |
agenda-group | 議程分組,只有獲取焦點的組中的規則才有可能觸發 |
timer | 定時器,指定規則觸發的時間 |
auto-focus | 自動獲取焦點,一般結合agenda-group一起使用 |
no-loop | 防止死循環 |
4.1 enabled屬性
enabled屬性對應的取值為true和false,默認值為true。
用于指定當前規則是否啟用,如果設置的值為false則當前規則無論是否匹配成功都不會觸發。
4.2 dialect 屬性
dialect 屬性用于指定當前規則使用的語言類型,取值為java和mvel默認為java
注:mvel是一種基于java語法的表達式語言。
mvel像正則表達式一樣,有直接支持集合、數組和字符串匹配的操作符。
mvel還提供了用來配置和構造字符串的模板語言。
mvel表達式內容包括屬性表達式,布爾表達式,方法調用,變量賦值,函數定義等。
4.3 salience屬性
salience屬性用于指定,規則的執行優先級,取值為Integer。數值越大越先執行,每個規則都有一個默認的執行順序,如果不設置salience屬性,規則體的執行順序為由上到下。
4.4no-loop 屬性
no-loop屬性用于防止死循環,當規則通過update之類的函數修改了Fact對象時,可能使當前規則再次被激活從而導致死循環。取值類型為Boolean,默認值為false。測試步驟如下:
第一步:編寫規則文件/resource/rules/noloop.drl
package testnoloop
import com.itheima.drools.entity.Student
/*此規則文件用于測試no-loop屬性
*/
rule "rule_noloop"when// no-loop true$student:Student(age == 25)thenupdate($student);//注意此處執行update會導致當前規則重新被激活System.out.println("規則rule_noloop觸發");
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();Student student = new Student();
student.setAge(25);//將數據提供給規則引擎,規則引擎會根據提供的數據進行規則匹配,如果規則匹配成功則執行規則
kieSession.insert(student);kieSession.fireAllRules();
kieSession.dispose();
通過控制臺可以看到,由于我們沒有設置no-loop屬性的值,所以發生了死循環。接下來設置no-loop的值為true再次測試則不會發生死循環。
4.5 activation-group屬性
activation-group屬性是指激活分組,取值為String類型。具有相同分組名稱的規則只能有一個規則被觸發。
第一步:編寫規則文件/resources/rules/activationgroup.drl
package testactivationgroup
/*此規則文件用于測試activation-group屬性
*/rule "rule_activationgroup_1"activation-group "mygroup"whenthenSystem.out.println("規則rule_activationgroup_1觸發");
endrule "rule_activationgroup_2"activation-group "mygroup"whenthenSystem.out.println("規則rule_activationgroup_2觸發");
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
通過控制臺可以發現,上面的兩個規則因為屬于同一個分組,所以只有一個觸發了。同一個分組中的多個規則如果都能夠匹配成功,具體哪一個最終能夠被觸發可以通過salience屬性確定。
4.6 agenda-group屬性
agenda-group屬性為議程分組,屬于另一種可控的規則執行方式。用戶可以通過設置agenda-group來控制規則的執行,只有獲取焦點的組中的規則才會被觸發。
第一步:創建規則文件/resources/rules/agendagroup.drl
package testagendagroup
/*此規則文件用于測試agenda-group屬性
*/
rule "rule_agendagroup_1"agenda-group "myagendagroup_1"whenthenSystem.out.println("規則rule_agendagroup_1觸發");
endrule "rule_agendagroup_2"agenda-group "myagendagroup_1"whenthenSystem.out.println("規則rule_agendagroup_2觸發");
end
//========================================================
rule "rule_agendagroup_3"agenda-group "myagendagroup_2"whenthenSystem.out.println("規則rule_agendagroup_3觸發");
endrule "rule_agendagroup_4"agenda-group "myagendagroup_2"whenthenSystem.out.println("規則rule_agendagroup_4觸發");
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();//設置焦點,對應agenda-group分組中的規則才可能被觸發
kieSession.getAgenda().getAgendaGroup("myagendagroup_1").setFocus();kieSession.fireAllRules();
kieSession.dispose();
通過控制臺可以看到,只有獲取焦點的分組中的規則才會觸發。與activation-group不同的是,activation-group定義的分組中只能夠有一個規則可以被觸發,而agenda-group分組中的多個規則都可以被觸發。
4.7 auto-focus屬性
auto-focus屬性為自動獲取焦點,取值類型為Boolean,默認值為false。一般結合agenda-group屬性使用,當一個議程分組未獲取焦點時,可以設置auto-focus屬性來控制。
第一步:修改/resources/rules/agendagroup.drl文件內容如下
package testagendagrouprule "rule_agendagroup_1"agenda-group "myagendagroup_1"whenthenSystem.out.println("規則rule_agendagroup_1觸發");
endrule "rule_agendagroup_2"agenda-group "myagendagroup_1"whenthenSystem.out.println("規則rule_agendagroup_2觸發");
end
//========================================================
rule "rule_agendagroup_3"agenda-group "myagendagroup_2"auto-focus true //自動獲取焦點whenthenSystem.out.println("規則rule_agendagroup_3觸發");
endrule "rule_agendagroup_4"agenda-group "myagendagroup_2"auto-focus true //自動獲取焦點whenthenSystem.out.println("規則rule_agendagroup_4觸發");
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
通過控制臺可以看到,設置auto-focus屬性為true的規則都觸發了。
4.8 timer屬性
timer屬性可以通過定時器的方式指定規則執行的時間,使用方式有兩種:
方式一:timer (int: <initial delay> <repeat interval>?)
此種方式遵循java.util.Timer對象的使用方式,第一個參數表示幾秒后執行,第二個參數表示每隔幾秒執行一次,第二個參數為可選。
方式二:timer(cron: <cron expression>)
此種方式使用標準的unix cron表達式的使用方式來定義規則執行的時間。
第一步:創建規則文件/resources/rules/timer.drl
package testtimer
import java.text.SimpleDateFormat
import java.util.Date
/*此規則文件用于測試timer屬性
*/rule "rule_timer_1"timer (5s 2s) //含義:5秒后觸發,然后每隔2秒觸發一次whenthenSystem.out.println("規則rule_timer_1觸發,觸發時間為:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
endrule "rule_timer_2"timer (cron:0/1 * * * * ?) //含義:每隔1秒觸發一次whenthenSystem.out.println("規則rule_timer_2觸發,觸發時間為:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
final KieSession kieSession = kieClasspathContainer.newKieSession();new Thread(new Runnable() {public void run() {//啟動規則引擎進行規則匹配,直到調用halt方法才結束規則引擎kieSession.fireUntilHalt();}
}).start();Thread.sleep(10000);
//結束規則引擎
kieSession.halt();
kieSession.dispose();
注意:單元測試的代碼和以前的有所不同,因為我們規則文件中使用到了timer進行定時執行,需要程序能夠持續一段時間才能夠看到定時器觸發的效果。
4.9 date-effective屬性
date-effective屬性用于指定規則的生效時間,即只有當前系統時間大于等于設置的時間或者日期規則才有可能觸發。默認日期格式為:dd-MMM-yyyy。用戶也可以自定義日期格式。
第一步:編寫規則文件/resources/rules/dateeffective.drl
package testdateeffective
/*此規則文件用于測試date-effective屬性
*/
rule "rule_dateeffective_1"date-effective "2020-10-01 10:00"whenthenSystem.out.println("規則rule_dateeffective_1觸發");
end
第二步:編寫單元測試
//設置日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
注意:上面的代碼需要設置日期格式,否則我們在規則文件中寫的日期格式和默認的日期格式不匹配程序會報錯。
4.10 date-expires屬性
date-expires屬性用于指定規則的失效時間,即只有當前系統時間小于設置的時間或者日期規則才有可能觸發。默認日期格式為:dd-MMM-yyyy。用戶也可以自定義日期格式。
第一步:編寫規則文件/resource/rules/dateexpires.drl
package testdateexpires
/*此規則文件用于測試date-expires屬性
*/rule "rule_dateexpires_1"date-expires "2019-10-01 10:00"whenthenSystem.out.println("規則rule_dateexpires_1觸發");
end
第二步:編寫單元測試
//設置日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();
注意:上面的代碼需要設置日期格式,否則我們在規則文件中寫的日期格式和默認的日期格式不匹配程序會報錯。
5 Drools 全局變量
global關鍵字用于在規則文件中定義全局變量,它可以讓應用程序的對象在規則文件中能夠被訪問。可以用來為規則文件提供數據或服務。
語法結構為:global 對象類型 對象名稱
在使用global定義的全局變量時有兩點需要注意:
1、如果對象類型為包裝類型時,在一個規則中改變了global的值,那么只針對當前規則有效,對其他規則中的global不會有影響。可以理解為它是當前規則代碼中的global副本,規則內部修改不會影響全局的使用。
2、如果對象類型為集合類型或JavaBean時,在一個規則中改變了global的值,對java代碼和所有規則都有效。
代碼如下:
第一步:創建UserService類
package com.itheima.drools.service;public class UserService {public void save(){System.out.println("UserService.save()...");}
}
第二步:編寫規則文件/resources/rules/global.drl
package testglobal
/*此規則文件用于測試global全局變量
*/global java.lang.Integer count //定義一個包裝類型的全局變量
global com.itheima.drools.service.UserService userService //定義一個JavaBean類型的全局變量
global java.util.List gList //定義一個集合類型的全局變量rule "rule_global_1"whenthencount += 10; //全局變量計算,只對當前規則有效,其他規則不受影響userService.save();//調用全局變量的方法gList.add("itcast");//向集合類型的全局變量中添加元素,Java代碼和所有規則都受影響gList.add("itheima");System.out.println("count=" + count);System.out.println("gList.size=" + gList.size());
endrule "rule_global_2"whenthenuserService.save();System.out.println("count=" + count);System.out.println("gList.size=" + gList.size());
end
第三步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();//設置全局變量,名稱和類型必須和規則文件中定義的全局變量名稱對應
kieSession.setGlobal("userService",new UserService());
kieSession.setGlobal("count",5);
List list = new ArrayList();//size為0
kieSession.setGlobal("gList",list);kieSession.fireAllRules();
kieSession.dispose();//因為在規則中為全局變量添加了兩個元素,所以現在的size為2
System.out.println(list.size());
5.2query查詢
query查詢提供了一種查詢working memory中符合約束條件的Fact對象的簡單方法。它僅包含規則文件中的LHS部分,不用指定“when”和“then”部分并且以end結束。具體語法結構如下:
query 查詢的名稱(可選參數)LHS
end
具體操作步驟:
第一步:編寫規則文件/resources/rules/query.drl
package testquery
import com.itheima.drools.entity.Student
/*此規則文件用于測試query查詢
*///不帶參數的查詢
//當前query用于查詢Working Memory中age>10的Student對象
query "query_1"$student:Student(age > 10)
end//帶有參數的查詢
//當前query用于查詢Working Memory中age>10同時name需要和傳遞的參數name相同的Student對象
query "query_2"(String sname)$student:Student(age > 20 && name == sname)
end
第二步:編寫單元測試
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();Student student1 = new Student();
student1.setName("張三");
student1.setAge(12);Student student2 = new Student();
student2.setName("李四");
student2.setAge(8);Student student3 = new Student();
student3.setName("王五");
student3.setAge(22);//將對象插入Working Memory中
kieSession.insert(student1);
kieSession.insert(student2);
kieSession.insert(student3);//調用規則文件中的查詢
QueryResults results1 = kieSession.getQueryResults("query_1");
int size = results1.size();
System.out.println("size=" + size);
for (QueryResultsRow row : results1) {Student student = (Student) row.get("$student");System.out.println(student);
}//調用規則文件中的查詢
QueryResults results2 = kieSession.getQueryResults("query_2","王五");
size = results2.size();
System.out.println("size=" + size);
for (QueryResultsRow row : results2) {Student student = (Student) row.get("$student");System.out.println(student);
}
//kieSession.fireAllRules();
kieSession.dispose();
5.3 function函數
function關鍵字用于在規則文件中定義函數,就相當于java類中的方法一樣。可以在規則體中調用定義的函數。使用函數的好處是可以將業務邏輯集中放置在一個地方,根據需要可以對函數進行修改。
函數定義的語法結構如下:
function 返回值類型 函數名(可選參數){//邏輯代碼
}
具體操作步驟:
第一步:編寫規則文件/resources/rules/function.drl
package testfunction
import com.itheima.drools.entity.Student
/*此規則文件用于測試function函數
*///定義一個函數
function String sayHello(String name){return "hello " + name;
}rule "rule_function_1"when$student:Student(name != null)then//調用上面定義的函數String ret = sayHello($student.getName());System.out.println(ret);
end
5.4 LHS加強
5.4.1 符合值限制in/not
復合值限制是指超過一直匹配值 的限制條件,類似于SQL語句中的in關鍵字,Drools規則體中的LHS部分可以使用in或者not in 進行復合值的匹配
舉例:
$s:Student(name in ("張三","李四","王五"))
$s:Student(name not in ("張三","李四","王五"))
5.4.2 條件元素eval
eval用于規則體的LHS部分,并返回一個Boolean類型的值。語法結構如下:
eval(表達式)
舉例:
eval(true)
eval(false)
eval(1 == 1)
5.4.3 條件元素not
not用于判斷Working Memory中是否存在某個Fact對象,如果不存在則返回true,如果存在則返回false。語法結構如下:
not Object(可選屬性約束)
舉例:
not Student()
not Student(age < 10)
5.4.4 條件元素exists
exists的作用與not相反,用于判斷Working Memory中是否存在某個Fact對象,如果存在則返回true,不存在則返回false。語法結構如下:
exists Object(可選屬性約束)
舉例:
exists Student()
exists Student(age < 10 && name != null)
可能有人會有疑問,我們前面在LHS部分進行條件編寫時并沒有使用exists也可以達到判斷Working Memory中是否存在某個符合條件的Fact元素的目的,那么我們使用exists還有什么意義?
兩者的區別:當向Working Memory中加入多個滿足條件的Fact對象時,使用了exists的規則執行一次,不使用exists的規則會執行多次。
例如:
規則文件(只有規則體):
rule "使用exists的規則"whenexists Student()thenSystem.out.println("規則:使用exists的規則觸發");
endrule "沒有使用exists的規則"whenStudent()thenSystem.out.println("規則:沒有使用exists的規則觸發");
end
Java代碼:
kieSession.insert(new Student());
kieSession.insert(new Student());
kieSession.fireAllRules();
上面第一個規則只會執行一次,因為Working Memory中存在兩個滿足條件的Fact對象,第二個規則會執行兩次。
5.4.5 規則繼承
規則之間可以使用extends關鍵字進行規則條件部分的繼承,類似于java類之間的繼承。
例如:
rule "rule_1"whenStudent(age > 10)thenSystem.out.println("規則:rule_1觸發");
endrule "rule_2" extends "rule_1" //繼承上面的規則when/*此處的條件雖然只寫了一個,但是從上面的規則繼承了一個條件,所以當前規則存在兩個條件,即Student(age < 20)和Student(age > 10)*/Student(age < 20) thenSystem.out.println("規則:rule_2觸發");
end
5.5 RHS加強
RHS部分是規則體的重要組成部分,當LHS部分的條件匹配成功后,對應的RHS部分就會觸發執行。一般在RHS部分中需要進行業務處理。
5.5.1halt
halt 方法的作用是立即終止后面所有規則的執行
package testhalt
rule "rule_halt_1"whenthenSystem.out.println("規則:rule_halt_1觸發");drools.halt();//立即終止后面所有規則執行
end//當前規則并不會觸發,因為上面的規則調用了halt方法導致后面所有規則都不會執行
rule "rule_halt_2"whenthenSystem.out.println("規則:rule_halt_2觸發");
end
5.5.2
getWorkingMemory
getWorkingMemory方法的作用是返回工作內存對象。
package testgetWorkingMemory
rule "rule_getWorkingMemory"whenthenSystem.out.println(drools.getWorkingMemory());
end
5.5.3 getRule
getRule方法的作用是返回規則對象。
package testgetRule
rule "rule_getRule"whenthenSystem.out.println(drools.getRule());
end
5.6 規則文件編碼規范
我們在進行drl規則文件編碼時應盡量遵循如下規范:
- 所有規則文件(.drl)統一放在一個規定的文件夾中,如:/rules文件夾
- 書寫的每個規則應盡量加上注釋
- 統一類型的對象盡量放在規則文件中,如所有Student類型的對象盡量放在一個規則文件中
- 規則結果部分(RHS)盡量不要有條件語句,如if(…),盡量不要有復雜的邏輯和深層次的嵌套語句
- 每個規則最好都加上salience屬性,明確執行順序
- Drools默認dialect為java盡量避免使用mvel
6. Spring整合Drools
6.1 Spring簡單整合Drools
在項目中使用Drools時往往會跟Spring整合來使用。具體整合步驟如下:
第一步:創建maven工程drools_spring并配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.itheima</groupId><artifactId>drools_spring</artifactId><version>1.0-SNAPSHOT</version><properties><drools.version>7.10.0.Final</drools.version><spring.version>5.0.5.RELEASE</spring.version></properties><dependencies><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>${drools.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.kie</groupId><artifactId>kie-spring</artifactId><version>${drools.version}</version><!--注意:此處必須排除傳遞過來的依賴,否則會跟我們自己導入的Spring jar包產生沖突--><exclusions><exclusion><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency></dependencies>
</project>
第二步:創建規則目錄/resources/rules,中rules目錄中創建規則文件helloworld.drl
package helloworldrule "rule_helloworld"wheneval(true)thenSystem.out.println("規則:rule_helloworld觸發...");
end
第三步:創建Spring配置文件/resources/spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:kie="http://drools.org/schema/kie-spring"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://drools.org/schema/kie-springhttp://drools.org/schema/kie-spring.xsd"><kie:kmodule id="kmodule"><kie:kbase name="kbase" packages="rules"><kie:ksession name="ksession"></kie:ksession></kie:kbase></kie:kmodule><bean class="org.kie.spring.annotations.KModuleAnnotationPostProcessor"></bean>
</beans>
第四步:編寫單元測試類
package com.itheima.test;import org.junit.Test;
import org.junit.runner.RunWith;
import org.kie.api.KieBase;
import org.kie.api.cdi.KBase;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class DroolsSpringTest {@KBase("kbase")private KieBase kieBase;//注入KieBase對象@Testpublic void test1(){KieSession kieSession = kieBase.newKieSession();kieSession.fireAllRules();kieSession.dispose();}
}
6.2 Spring Boot整合Drools
目前在企業開發中Spring Boot已經成為主流,主要進行Spring Boot整合Drools。具體操作步驟:
第一步:創建maven工程drools_springboot并配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starters</artifactId><version>2.0.6.RELEASE</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.itheima</groupId><artifactId>drools_springboot</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><!--drools規則引擎--><dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>7.6.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.6.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-templates</artifactId><version>7.6.0.Final</version></dependency><dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>7.6.0.Final</version></dependency><dependency><groupId>org.kie</groupId><artifactId>kie-spring</artifactId><exclusions><exclusion><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></exclusion></exclusions><version>7.6.0.Final</version></dependency></dependencies><build><finalName>${project.artifactId}</finalName><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes><filtering>false</filtering></resource></resources><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.3.2</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>
</project>
第二步:創建/resources/application.yml文件
server:port: 8080
spring:application:name: drools_springboot
第三步:創建規則文件/resources/rules/helloworld.drl
package helloworld
rule "rule_helloworld"wheneval(true)thenSystem.out.println("規則:rule_helloworld觸發...");
end
第四步:編寫配置類DroolsConfig
package com.itheima.drools.config;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.Resource;
import java.io.IOException;
/*** 規則引擎配置類*/
@Configuration
public class DroolsConfig {//指定規則文件存放的目錄private static final String RULES_PATH = "rules/";private final KieServices kieServices = KieServices.Factory.get();@Bean@ConditionalOnMissingBeanpublic KieFileSystem kieFileSystem() throws IOException {KieFileSystem kieFileSystem = kieServices.newKieFileSystem();ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();Resource[] files = resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.*");String path = null;for (Resource file : files) {path = RULES_PATH + file.getFilename();kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));}return kieFileSystem;}@Bean@ConditionalOnMissingBeanpublic KieContainer kieContainer() throws IOException {KieRepository kieRepository = kieServices.getRepository();kieRepository.addKieModule(kieRepository::getDefaultReleaseId);KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());kieBuilder.buildAll();return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());}@Bean@ConditionalOnMissingBeanpublic KieBase kieBase() throws IOException {return kieContainer().getKieBase();}@Bean@ConditionalOnMissingBeanpublic KModuleBeanFactoryPostProcessor kiePostProcessor() {return new KModuleBeanFactoryPostProcessor();}
}
第五步:創建RuleService類
package com.itheima.drools.service;import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class RuleService {@Autowiredprivate KieBase kieBase;public void rule(){KieSession kieSession = kieBase.newKieSession();kieSession.fireAllRules();kieSession.dispose();}
}
第六步:創建HelloController類
package com.itheima.drools.controller;import com.itheima.drools.service.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/hello")
public class HelloController {@Autowiredprivate RuleService ruleService;@RequestMapping("/rule")public String rule(){ruleService.rule();return "OK";}
}
第七步:創建啟動類DroolsApplication
package com.itheima.drools;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DroolsApplication {public static void main(String[] args) {SpringApplication.run(DroolsApplication.class,args);}
}
第八步:啟動服務,訪問http://localhost:8080/hello/rule
頁面返回OK