Drools 规则引擎一文读懂
目錄
一、Drools 簡介
電商平臺的促銷活動
規則引擎
Drools 簡介
二、Drools 快速入門?
電商平臺促銷積分規則
開發實現
三、Drools 規則引擎構成及其核心類
Drools規則引擎構成
Drools規則引擎概念
四、Drools 基礎語法
規則文件構成
?規則體語法結構
規則注釋
Pattern模式匹配
規則比較操作符
操作步驟?
執行指定規則?
關鍵字
?五、Drools 規則屬性 attributes
enabled屬性?
dialect屬性
salience屬性?
no-loop屬性
?lock-on-active屬性
activation-group屬性
agenda-group屬性
auto-focus屬性
timer屬性
date-effective屬性
date-expires屬性
六、Drools高級語法
?global全局變量
query查詢
?function函數
條件-LHS加強
復合值限制in/not in
條件元素eval
條件元素not
條件元素exists
規則繼承
結果-RHS
insert方法
update方法
modify方法
retract/delete方法
RHS加強
halt
getWorkingMemory
getRule
規則文件編碼規范
七、WorkBench
?WorkBench簡介
配置 Tomcat
啟動服務器
WorkBench使用?
創建空間、項目
創建數據對象和drl文件
設置KieBase+KieSession
代碼使用?
一、Drools 簡介
電商平臺的促銷活動
????????小明是一家互聯網公司的軟件工程師,他們公司為了吸引新用戶經常會搞活動,小明常常為了做活動加班加點很煩躁,這不今天呀又來了一個活動需求,我們大家一起幫他看看。
????????活動規則是根據用戶購買訂單的金額給用戶送相應的積分,購買的越多送的積分越多,用戶可以使用積分來兌換相應的商品,我們這次活動的力度很大,肯定會吸引很多的用戶參加,產品經理小王興高采烈唾液橫飛的對小明講到。小明心想,又tm來這套,這次需求又要變更多少次呢?表面上還的配合,說趕緊把規則給我們吧,早點開發早點上線,小王說這次需求老簡單啦,估計你們兩個小時就搞定了,不信你看需求文檔。
100元以下, 不加分 100元-500元 加100分 500元-1000元 加500分 1000元 以上 加1000分小明一看,這需求果然簡單呀,作為一個工作了兩三年的程序員來講,這不就是小case,半天搞定,送積分的核心代碼如下:
public void execute() throws Exception { List<Order> orderList = getInitData();for (int i=0; i<orderList.size(); i++){ Order order = orderList.get(i); if (order.getAmout() <= 100){ order.setScore(0); addScore(order); }else if(order.getAmout() > 100 && order.getAmout() <= 500){ order.setScore(100); addScore(order); }else if(order.getAmout() > 500 && order.getAmout() <= 1000){ order.setScore(500); addScore(order); }else{ order.setScore(1000); addScore(order); } } }????????上線運行了半天之后,財務部的小財突然監測到活動賬戶的金額大為減少,發現產品做活動竟然沒有通知到他,非常不爽,于是給領導小馬說,這樣大規模的活動,對公司財務有壓力,領導小馬權衡了一番說,這樣吧活動繼續,但是金額翻倍再送積分,于是規則變成了這樣:200元以下不給積分,1000元以下給100積分…
????????小明看領導都發話了,沒辦法改呀,不過也簡單,就是將里面的值都翻了倍,在投產上去,只是挨了不少測試的白眼。
????????活動又進行了一天,運營人員通過后臺監控發現提到2倍以后,用戶積極性變的很差,活動效果不理想,和領導商議了一下,改為最初規則的1.5倍,及150元一下不給積分,750元以下給100積分… 小明這時候的心情大概是這樣子的,一萬個下圖動物狂奔而過。
????????沒辦法還得改不是,當然這次小明可學乖了,將這些數據(多少元送多少分)存到了數據庫中,當老板再改主意的時候,只要改一下數據庫的值就可以了,小明為自己的明聰明到有點小高興。?
核心代碼:
public void execute() throws Exception { List<Order> orderList = getInitData();List<int> values = getTableValues();for (int i=0; i<orderList.size(); i++){ Order order = orderList.get(i); if (order.getAmout() <= values.get(0)){ order.setScore(values.get(3)); addScore(order); }else if(order.getAmout() > values.get(0) && order.getAmout() <= values.get(1)){ order.setScore(values.get(4)); addScore(order); }else if(order.getAmout() > values.get(1) && order.getAmout() <= values.get(2)){ order.setScore(values.get(5)); addScore(order); }else{ order.setScore(values.get(6)); addScore(order); } } }????????正當小明得意洋洋的打了個最新版本投產上線之后,產品經理小王說積分規則層次太少了,由以前的4組變成8組,小明此刻的心情:kao …
????????小明想這樣下去非得被他們弄死,必須要找找有什么技術可以將活動規則和代碼解耦,不管規則如何變化,執行端不用動。小明搜了半天還真有這樣的東西,那就是規則引擎,那么規則引擎到底是什么東西呢?我們來看看。
規則引擎
????????規則引擎:全稱為業務規則管理系統,英文名為BRMS。規則引擎的主要思想是將應用程序中的業務決策部分分離出來,并使用預定義的語義模塊編寫業務決策(業務規則),由用戶或 開發者在需要時進行配置、管理。需要注意的是規則引擎并不是一個具體的技術框架,而是指的一類系統,即業務規則管理系統。
????????java開源的規則引擎有:Drools、Easy Rules、Mandarax、IBM ILOG。使用最為廣泛并且開源的是Drools。
主要應用場景:
????????對于一些存在比較復雜的業務規則并且業務規則會頻繁變換的系統比較適合使用規則引擎,如下:
- 風控系統-------風險貸款、風險評估
- 反欺詐項目-----銀行貸款、征信驗證
- 決策平臺-------財務計算
- 電商平臺------滿減、打折、加價購
Drools 簡介
????????Drools 是用 Java 語言編寫的開放源碼規則引擎,使用 Rete 算法對所編寫的規則求值。Drools 允許使用聲明方式表達業務邏輯。可以使用非 XML 的本地語言編寫規則,從而便于學習和理解。并且,還可以將 Java 代碼直接嵌入到規則文件中,這令 Drools 的學習更加吸引人。
Drools 還具有其他優點:
- 非常活躍的社區支持
- 易用
- 快速的執行速度
- 在 Java 開發人員中流行
- 與 Java Rule Engine API(JSR 94)兼容
Drools 是業務邏輯集成平臺,被分為4個項目:
- Drools Guvnor (BRMS/BPMS):業務規則管理系統
- Drools Expert (rule engine):規則引擎,drools的核心部分
- Drools Flow (process/workflow):工作流引擎
- Drools Fusion (cep/temporal reasoning):事件處理
官網:http://www.drools.org/#
官方文檔:http://www.drools.org/learn/documentation.html
在項目中使用drools 時,即可以單獨使用也可以整合spring使用,如果是單獨使用僅需要導入以下maven 依賴即可。
<!--添加規則引擎依賴--><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.73.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>7.73.0.Final</version></dependency>Drools API 開發步驟如下:
二、Drools 快速入門?
電商平臺促銷積分規則
100元以下, 不加分 100元-500元 加100分 500元-1000元 加500分 1000元 以上 加1000分開發實現
第一步:創建Maven項目,添加Drools 規則引擎依賴。
<!--添加規則引擎依賴--><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.73.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>7.73.0.Final</version></dependency><!--添加單元測試工具類--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency>第二步:根據drools要求創建resources/META-INF/kmodule.xml 配置文件
需要有一個配置文件告訴代碼規則文件drl 在哪里,在drools中這個文件就是kmodule.xml。
<?xml version="1.0" encoding="UTF-8"?> <kmodule xmlns="http://www.drools.org/xsd/kmodule"><!--name:指定kbase 名稱,可以是任意但必須唯一packages:指定規則文件存放目錄,依據實際情況進行填寫default:指定當前的kbase 是否為默認--><kbase name="rules" packages="rules" default="true"><!--name:指定ksession名稱,可以是任意但必須唯一default:指定ksession 是否為默認--><ksession name="ksession-rules" default="true"/></kbase> </kmodule>第三步:創建業務實體對象
package com.zzg.model;public class Order implements java.io.Serializable {private int amount; //訂單金額private int score; //訂單積分@Overridepublic String toString() {return "Order{" +"amount=" + amount +", score=" + score +'}';}public int getAmount() {return amount;}public void setAmount(int amount) {this.amount = amount;}public int getScore() {return score;}public void setScore(int score) {this.score = score;} }第四步:創建規則文件,在resources/rules/score-rules.drl
package rules;import com.zzg.model.Order;// 100 元以下不加分 rule "score_1" when$order:Order(amount<100); then$order.setScore(0);System.out.println("觸發100元以下,不加積分"); end // 100-500 元, 加100積分 rule "score_2" when$order:Order(amount>100 && amount <= 500); then$order.setScore(100);System.out.println("觸發100-500 元, 加100積分"); end // 500-10000 元, 加500積分 rule "score_3" when$order:Order(amount>500 && amount <= 1000); then$order.setScore(500);System.out.println("觸發500-1000 元, 加500積分"); end // 大于1000 元, 加1000積分 rule "score_4" when$order:Order(amount>1000); then$order.setScore(1000);System.out.println("觸發大于1000 元, 加1000積分"); end第五步:單元測試
package com.zzg;import com.zzg.model.Order; import org.junit.Test; import org.kie.api.KieServices; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession;public class TestDrools {@Testpublic void test1() {// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession();// 業務對象Order order = new Order();order.setAmount(100);// 第四步kieSession.insert(order);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();System.out.println("指定規則引擎后的結果:" + order.getScore());} }效果截圖:
23:01:01.112 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: rules 23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE 23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED 指定規則引擎后的結果:0三、Drools 規則引擎構成及其核心類
Drools規則引擎構成
drools規則引擎由以下幾部分構成:
- Working Memory(工作內存)
- Rules(規則庫)
- Inference?Engine(推理引擎)
其中Inference Engine(t推理引擎)又包含如下:
- Pattern Match(匹配器)具體匹配那一個規則,由它來完成
- Agenda(議程)
- Execution Engine(執行引擎)
Drools規則引擎概念
Working Memory:工作內存,drools規則引擎會從Working Memory中獲取數據并和規則文件中定義的規則進行模式匹配,所以我們開發的應用程序只需要將我們的數據插入到Working Memory中即可,例如本案例中我們調用kieSession.insert(order)就是將order對象插入到了工作內存中。
Fact:事實,是指在drools 規則應用當中,將一個普通的JavaBean插入到Working Memory后的對象就是Fact對象,例如本案例中的Order對象就屬于Fact對象。Fact對象是我們的應用和規則引擎進行數據交互的橋梁或通道。
Rules:規則庫,我們在規則文件中定義的規則都會被加載到規則庫中。
Pattern Matcher:匹配器,將Rule Base中的所有規則與Working Memory中的Fact對象進行模式匹配,匹配成功的規則將被激活并放入Agenda中。
Agenda:議程,用于存放通過匹配器進行模式匹配后被激活的規則。
四、Drools 基礎語法
規則文件構成
drl是Drools Rule Language的縮寫。
一套完整的規則文件內容構成如下:
| package | 包名,只限于邏輯上的管理,同一個包名下的查詢或者函數可以直接調用 |
| import | 用于導入類或者靜態方法 |
| global | 全局變量 |
| function | 自定義函數 |
| query | 查詢 |
| rule end | 規則體 |
?規則體語法結構
一個規則通常包含三個部分:屬性部分(attribute)、條件部分(LHS)和結果部分(RHS)
語法結構體:
rule "ruleName"attributeswhenLHS thenRHS endrule:關鍵字,表示規則開始,參數為規則的唯一名稱。
attributes:規則屬性,是rule與when之間的參數,為可選項。
when:關鍵字,后面跟規則的條件部分。
LHS(Left Hand Side):是規則的條件部分的通用名稱。它由零個或多個條件元素組成。如果LHS為空,則它將被視為始終為true的條件元素。 (左手邊)
then:關鍵字,后面跟規則的結果部分。
RHS(Right Hand Side):是規則的后果或行動部分的通用名稱。 (右手邊)
end:關鍵字,表示一個規則結束。
規則注釋
在drl形式的規則文件中使用注釋和Java類中使用注釋一致,分為單行注釋和多行注釋。
單行注釋用"//"進行標記,多行注釋以"/"開始,以"/"結束。如下示例:
//規則rule1的注釋,這是一個單行注釋 rule "rule1"whenthenSystem.out.println("rule1觸發"); end/* 規則rule2的注釋, 這是一個多行注釋 */ rule "rule2"whenthenSystem.out.println("rule2觸發"); endPattern模式匹配
????????Drools中的匹配器可以將Rule Base中的所有規則與Working Memory中的Fact對象進行模式匹配,那么我們就需要在規則體的LHS部分定義規則并進行模式匹配。LHS部分由一個或者多個條件組成,條件又稱為pattern。
語法:
pattern的語法結構為:綁定變量名:Object(Field約束)
其中綁定變量名可以省略,通常綁定變量名的命名一般建議以$開始。如果定義了綁定變量名,就可以在規則體的RHS部分使用此綁定變量名來操作相應的Fact對象。Field約束部分是需要返回true或者false的0個或多個表達式。
入門案例中:
package rules;import com.zzg.model.Order;// 100 元以下不加分 rule "score_1" when$order:Order(amount<100); then$order.setScore(0);System.out.println("觸發100元以下,不加積分"); end通過上面的例子我們可以知道,匹配的條件為:
1、工作內存中必須存在Order這種類型的Fact對象-----類型約束
2、Fact對象的price屬性值必須小于100------屬性約束
以上條件必須同時滿足當前規則才有可能被激活。
綁定變量既可以用在對象上,也可以用在對象的屬性上。入門案例如下:
// 100-500 元, 加100積分 rule "score_2" when$order:Order($fieldAmount:amount > 100 && amount <= 500); thenSystem.out.println("$fieldAmount=" + $fieldAmount);$order.setScore(100);System.out.println("觸發100-500 元, 加100積分"); endLHS部分還可以定義多個pattern,多個pattern之間可以使用and或者or進行連接,也可以不寫,默認連接為and。
規則比較操作符
Drools提供的比較操作符,如下表:
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| == | 等于 |
| != | 不等于 |
| contains | 檢查一個Fact對象的某個屬性值是否包含一個指定的對象值 |
| not contains | 檢查一個Fact對象的某個屬性值是否不包含一個指定的對象值 |
| memberOf | 判斷一個Fact對象的某個屬性是否在一個或多個集合中 |
| not memberOf | 判斷一個Fact對象的某個屬性是否不在一個或多個集合中 |
| matches | 判斷一個Fact對象的屬性是否與提供的標準的Java正則表達式進行匹配 |
| not matches | 判斷一個Fact對象的屬性是否不與提供的標準的Java正則表達式進行匹配 |
操作步驟?
第一步:創建實體類,用于測試比較操作符
package com.zzg.model;import java.util.List;public class ComparisonEntity implements java.io.Serializable {/*** 名字集合*/private String names;/*** 字符串集合*/private List<String> list;@Overridepublic String toString() {return "ComparisonEntity{" +"names='" + names + '\'' +", list=" + 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下創建規則文件comparison.drl
package rulesimport com.zzg.model.ComparisonEntity;/*用于測試Drools提供的比較操作符 *///測試比較操作符contains rule "rule_comparison_contains"whenComparisonEntity(names contains "張三")ComparisonEntity(list contains names)thenSystem.out.println("規則rule_comparison_contains觸發"); end//測試比較操作符not contains rule "rule_comparison_notContains"whenComparisonEntity(names not contains "張三")ComparisonEntity(list not contains names)thenSystem.out.println("規則rule_comparison_notContains觸發"); end//測試比較操作符memberOf rule "rule_comparison_memberOf"whenComparisonEntity(names memberOf list)thenSystem.out.println("規則rule_comparison_memberOf觸發"); end//測試比較操作符not memberOf rule "rule_comparison_notMemberOf"whenComparisonEntity(names not memberOf list)thenSystem.out.println("規則rule_comparison_notMemberOf觸發"); end//測試比較操作符matches rule "rule_comparison_matches"whenComparisonEntity(names matches "張.*")thenSystem.out.println("規則rule_comparison_matches觸發"); end//測試比較操作符not matches rule "rule_comparison_notMatches"whenComparisonEntity(names not matches "張.*")thenSystem.out.println("規則rule_comparison_notMatches觸發"); end第三步:編寫單元測試
@Testpublic void test2() {// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession();// 業務對象ComparisonEntity comparisonEntity = new ComparisonEntity();comparisonEntity.setNames("張三");List<String> list = new ArrayList<>();list.add("張三");list.add("李四");comparisonEntity.setList(list);// 第四步kieSession.insert(comparisonEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();}效果截圖:
09:52:42.457 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 規則rule_comparison_contains觸發 規則rule_comparison_memberOf觸發 規則rule_comparison_matches觸發 09:52:42.488 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING 09:52:42.488 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE 09:52:42.488 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED執行指定規則?
????????在調用規則代碼時,滿足條件的規則都會被執行。那么如果我們只想執行其中的某個規則如何實現呢?
????????Drools給我們提供的方式是通過規則過濾器來實現執行指定規則。對于規則文件不用做任何修改,只需要修改Java代碼即可,如下:
//通過規則過濾器實現只執行指定規則 kieSession.fireAllRules(new kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule 名稱"));關鍵字
Drools的關鍵字分為:硬關鍵字(Hard keywords)和軟關鍵字(Soft keywords)。
硬關鍵字是我們在規則文件中定義包名或者規則名時明確不能使用的,否則程序會報錯。軟關鍵字雖然可以使用,但是不建議使用。
硬關鍵字包括:true false null
軟關鍵字包括:lock-on-active date-effective date-expires no-loop auto-focus activation-group agenda-group ruleflow-group entry-point duration package import dialect salience enabled attributes rule extend when then template query declare function global eval not in or and exists forall accumulate collect from action reverse result end over init
示例:
rule true //不可以
rule "true" 可以
?五、Drools 規則屬性 attributes
前面我們已經知道了規則體的構成如下:
rule "ruleName"attributeswhenLHSthenRHS end本章節就是針對規則體的attributes屬性部分進行講解。Drools中提供的屬性如下表(部分屬性):
| salience | 指定規則執行優先級 |
| dialect | 指定規則使用的語言類型,取值為java和mvel |
| enabled | 指定規則是否啟用 |
| date-effective | 指定規則生效時間 |
| date-expires | 指定規則失效時間 |
| activation-group | 激活分組,具有相同分組名稱的規則只能有一個規則觸發 |
| agenda-group | 議程分組,只有獲取焦點的組中的規則才有可能觸發 |
| timer | 定時器,指定規則觸發的時間 |
| auto-focus | 自動獲取焦點,一般結合agenda-group一起使用 |
| no-loop | 防止死循環,防止自己更新規則再次觸發 |
| lock-on-active | no-loop增強版本。可防止別人更新規則再次出發 |
enabled屬性?
enabled屬性對應的取值為true和false,默認值為true。
用于指定當前規則是否啟用,如果設置的值為false則當前規則無論是否匹配成功都不會觸發
第一步:創建實體類,用于測試屬性attributes
package com.zzg.model;public class AttributesEnabledEntity implements java.io.Serializable {private int num;@Overridepublic String toString() {return "AttributesEnabledEntity{" +"num=" + num +'}';}public int getNum() {return num;}public void setNum(int num) {this.num = num;} }第二步:在/resources/rules下創建規則文件attributes-rules.drl
package rules;import com.zzg.model.AttributesEnabledEntity;/*用于測試Drools 屬性:enabled *///測試enabled rule "rule_attributes_enabled"enabled falsewhenAttributesEnabledEntity(num > 10)thenSystem.out.println("規則rule_attributes_enabled觸發"); end第三步:編寫單元測試
@Testpublic void test4() {// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession();// 業務對象AttributesEnabledEntity enabledEntity = new AttributesEnabledEntity();enabledEntity.setNum(11);// 第四步kieSession.insert(enabledEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();}dialect屬性
dialect屬性用于指定當前規則使用的語言類型,取值為java和mvel,默認值為java。
注:mvel是一種基于java語法的表達式語言。
雖然mvel吸收了大量的java語法,但作為一個表達式語言,還是有著很多重要的不同之處,以達到更高的效率,比如:mvel像正則表達式一樣,有直接支持集合、數組和字符串匹配的操作符。
除了表達式語言外,mvel還提供了用來配置和構造字符串的模板語言。
mvel2.x表達式包含以下部分的內容:屬性表達式,布爾表達式,方法調用,變量賦值,函數定義
salience屬性?
????????salience屬性用于指定規則的執行優先級,取值類型為Integer。數值越大越優先執行。每個規則都有一個默認的執行順序,如果不設置salience屬性,規則體的執行順序為由上到下。
attributes-rules.drl文件添加如下內容:
/*用于測試Drools 屬性:salience */rule "rule_attributes_salience_1"salience 10whenAttributesEnabledEntity(flag == true)thenSystem.out.println("規則 rule_attributes_salience_1 觸發"); endrule "rule_attributes_salience_2"salience 20whenAttributesEnabledEntity(flag == true)thenSystem.out.println("規則 rule_attributes_salience_2 觸發"); endrule "rule_attributes_salience_3"salience 1whenAttributesEnabledEntity(flag == true)thenSystem.out.println("規則 rule_attributes_salience_3 觸發"); endAttributesEnabledEntity 類新增boolean 類型(flag字段)
private boolean flag;效果截圖:
10:39:16.506 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES 規則 rule_attributes_salience_2 觸發 規則 rule_attributes_salience_1 觸發 規則 rule_attributes_salience_3 觸發no-loop屬性
????????no-loop屬性用于防止死循環,當規則通過update之類的函數修改了Fact對象時,可能使當前規則再次被激活從而導致死循環。取值類型為Boolean,默認值為false。測試步驟如下:
attributes-rules.drl文件添加如下內容:
/*用于測試Drools 屬性:no-loop */rule "rule_attributes_noloop"//no-loop truewhen$attributesNoLoopEntity:AttributesEnabledEntity(num > 1)thenupdate($attributesNoLoopEntity)System.out.println("規則 rule_attributes_noloop 觸發"); end第二步:編寫單元測試
@Testpublic void test4() {// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession();// 業務對象AttributesEnabledEntity enabledEntity = new AttributesEnabledEntity();enabledEntity.setNum(11);enabledEntity.setFlag(true);// 第四步kieSession.insert(enabledEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();}通過控制臺可以看到,由于我們沒有設置no-loop屬性的值,所以發生了死循環。接下來設置no-loop的值為true再次測試則不會發生死循環。
?lock-on-active屬性
lock-on-active這個屬性,可以限制當前規則只會被執行一次,包括當前規則的重復執行不是本身觸發的。取值類型為Boolean,默認值為false。測試步驟如下:
attributes-rules.drl文件添加如下內容:
/*用于測試Drools 屬性:lock-on-active */rule "rule_attributes_lock_on_active_1"no-loop truewhen$attributesLockOnActiveEntity:AttributesEnabledEntity(num > 1)thenupdate($attributesLockOnActiveEntity)System.out.println("規則 rule_attributes_lock_on_active_1 觸發"); endrule "rule_attributes_lock_on_active_2"no-loop truelock-on-active truewhen$attributesLockOnActiveEntity:AttributesEnabledEntity(num > 1)thenupdate($attributesLockOnActiveEntity)System.out.println("規則 rule_attributes_lock_on_active_2 觸發"); end編寫單元測試:
@Testpublic void test4() {// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession();// 業務對象AttributesEnabledEntity enabledEntity = new AttributesEnabledEntity();enabledEntity.setNum(11);enabledEntity.setFlag(true);// 第四步kieSession.insert(enabledEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();}????????no-loop的作用是限制因為modify等更新操作導致規則重復執行,但是有一個限定條件,是當前規則中進行更新導致當前規則重復執行。而不是防止其他規則更新相同的fact對象,導致當前規則更新,lock-on-active可以看作是no-loop的加強版,不僅能限制自己的更新,還能限制別人的更新造成的死循環。
activation-group屬性
activation-group屬性是指激活分組,取值為String類型。具有相同分組名稱的規則只能有一個規則被觸發。
第一步:編寫規則文件
package rules import com.zzg.model.AttributesActivationGroupEntity/*用于測試Drools 屬性: activation-group */rule "rule_attributes_activation_group_1"activation-group "customGroup"when$attributesActivationGroupEntity:AttributesActivationGroupEntity(num > 1)thenSystem.out.println("規則 rule_attributes_activation_group_1 觸發"); endrule "rule_attributes_activation_group_2"activation-group "customGroup"when$attributesActivationGroupEntity:AttributesActivationGroupEntity(num > 1)thenSystem.out.println("規則 rule_attributes_activation_group_2 觸發"); end編寫單元測試:
// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession(); // 業務對象 AttributesActivationGroupEntity attributesActivationGroupEntity = new AttributesActivationGroupEntity();attributesActivationGroupEntity.setNum(20);kieSession.insert(attributesActivationGroupEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();????????通過控制臺可以發現,上面的兩個規則因為屬于同一個分組,所以只有一個觸發了。同一個分組中的多個規則如果都能夠匹配成功,具體哪一個最終能夠被觸發可以通過salience屬性確定。
agenda-group屬性
agenda-group屬性為議程分組,屬于另一種可控的規則執行方式。用戶可以通過設置agenda-group來控制規則的執行,只有獲取焦點的組中的規則才會被觸發。
第一步:編寫規則文件
package rules import com.zzg.model.AttributesAgendaGroupEntity/*用于測試Drools 屬性: agenda-group */rule "rule_attributes_agenda_group_1"agenda-group "customAgendaGroup1"when$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)thenSystem.out.println("規則 rule_attributes_agenda_group_1 觸發"); endrule "rule_attributes_agenda_group_2"agenda-group "customAgendaGroup1"when$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)thenSystem.out.println("規則 rule_attributes_agenda_group_2 觸發"); endrule "rule_attributes_activation_group_3"agenda-group "customAgendaGroup2"when$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)thenSystem.out.println("規則 rule_attributes_activation_group_3 觸發"); endrule "rule_attributes_agenda_group_4"agenda-group "customAgendaGroup2"when$attributesAgendaGroupEntity:AttributesAgendaGroupEntity(num > 1)thenSystem.out.println("規則 rule_attributes_agenda_group_4 觸發"); end編寫單元測試:
// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 AttributesAgendaGroupEntity attributesAgendaGroupEntity = new AttributesAgendaGroupEntity();attributesAgendaGroupEntity.setNum(20);kieSession.insert(attributesAgendaGroupEntity);kieSession.getAgenda().getAgendaGroup("customAgendaGroup2").setFocus();// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();通過控制臺可以看到,只有獲取焦點的分組中的規則才會觸發。與activation-group不同的是,activation-group定義的分組中只能夠有一個規則可以被觸發,而agenda-group分組中的多個規則都可以被觸發。
auto-focus屬性
auto-focus屬性為自動獲取焦點,取值類型為Boolean,默認值為false。一般結合agenda-group屬性使用,當一個議程分組未獲取焦點時,可以設置auto-focus屬性來控制。
第一步:編寫規則文件
package rules import com.zzg.model.AttributesAutoFocusEntity/*用于測試Drools 屬性: auto-focus */rule "rule_attributes_auto_focus_1"agenda-group "customAgendaGroup1"when$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)thenSystem.out.println("規則 rule_attributes_auto_focus_1 觸發"); endrule "rule_attributes_auto_focus_2"agenda-group "customAgendaGroup1"when$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)thenSystem.out.println("規則 rule_attributes_auto_focus_2 觸發"); endrule "rule_attributes_auto_focus_3"agenda-group "customAgendaGroup2" // auto-focus truewhen$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)thenSystem.out.println("規則 rule_attributes_auto_focus_3 觸發"); endrule "rule_attributes_auto_focus_4"agenda-group "customAgendaGroup2"when$attributesAutoFocusEntity:AttributesAutoFocusEntity(num > 1)thenSystem.out.println("規則 rule_attributes_auto_focus_4 觸發"); end編寫單元測試
// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 AttributesAutoFocusEntity attributesAutoFocusEntity = new AttributesAutoFocusEntity();attributesAutoFocusEntity.setNum(20);kieSession.insert(attributesAutoFocusEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();通過控制臺可以看到,設置auto-focus屬性為true的規則都觸發了。
注意:同一個組,只要有個設置auto-focus true 其他的設置不設置都無所謂啦。都會起作用的。
timer屬性
timer屬性可以通過定時器的方式指定規則執行的時間,使用方式有兩種:
方式一:timer (int:??)
此種方式遵循java.util.Timer對象的使用方式,第一個參數表示幾秒后執行,第二個參數表示每隔幾秒執行一次,第二個參數為可選。
方式二:timer(cron:?)
此種方式使用標準的unix cron表達式的使用方式來定義規則執行的時間。
第一步:編寫規則文件
package rules import com.zzg.model.AttributesTimerEntity/*用于測試Drools 屬性: timer */rule "rule_attributes_timer_1"timer(5s 2s)when$attributesTimerEntity:AttributesTimerEntity(num > 1)thenSystem.out.println("規則 rule_attributes_timer_1 觸發"); endrule "rule_attributes_timer_2"timer(cron:0/1 * * * * ?)when$attributesTimerEntity:AttributesTimerEntity(num > 1)thenSystem.out.println("規則 rule_attributes_timer_2 觸發"); end編寫單元測試:
// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 AttributesTimerEntity attributesTimerEntity = new AttributesTimerEntity();attributesTimerEntity.setNum(20);kieSession.insert(attributesTimerEntity);kieSession.fireUntilHalt();Thread.sleep(10000);kieSession.halt(); // 關閉kieSession.dispose();注意:如果規則中有用到了timer屬性,匹配規則需要調用kieSession.fireUntilHalt();這里涉及一個規則引擎的執行模式和線程問題,關于具體細節,我們后續討論。
date-effective屬性
date-effective屬性用于指定規則的生效時間,即只有當前系統時間大于等于設置的時間或者日期規則才有可能觸發。默認日期格式為:dd-MMM-yyyy。用戶也可以自定義日期格式。
第一步:編寫規則文件
package rules import com.zzg.model.AttributesDateEffectiveEntity/*用于測試Drools 屬性: date-effective */rule "rule_attributes_date_effective" // date-effective "20-七月-2021"date-effective "2021-02-20"when$attributesDateEffectiveEntity:AttributesDateEffectiveEntity(num > 1)thenSystem.out.println("規則 rule_attributes_date_effective 觸發"); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 AttributesDateEffectiveEntity attributesDateEffectiveEntity = new AttributesDateEffectiveEntity();attributesDateEffectiveEntity.setNum(20);kieSession.insert(attributesDateEffectiveEntity); // 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();注意:需要在VM參數上加上日期格式:-Ddrools.dateformat=yyyy-MM-dd,在生產環境所在規則引擎的JVM設置中,也需要設置此參數,以保證開發和生產的一致性。
date-expires屬性
date-expires屬性用于指定規則的失效時間,即只有當前系統時間小于設置的時間或者日期規則才有可能觸發。默認日期格式為:dd-MMM-yyyy。用戶也可以自定義日期格式。
第一步:編寫規則文件/resource/rules/dateexpires.drl
package rules import com.zzg.model.AttributesDateExpiresEntity/*用于測試Drools 屬性: date-expires */rule "rule_attributes_date_expires"date-expires "2021-06-20"when$attributesDateExpiresEntity:AttributesDateExpiresEntity(num > 1)thenSystem.out.println("規則 rule_attributes_date_expires 觸發"); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 AttributesDateExpiresEntity attributesDateExpiresEntity = new AttributesDateExpiresEntity();attributesDateExpiresEntity.setNum(20);kieSession.insert(attributesDateExpiresEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();注意:需要在VM參數上加上日期格式:-Ddrools.dateformat=yyyy-MM-dd,在生產環境所在規則引擎的JVM設置中,也需要設置此參數,以保證開發和生產的一致性。
六、Drools高級語法
我們已經知道了一套完整的規則文件內容構成如下:
| package | 包名,只限于邏輯上的管理,同一個包名下的查詢或者函數可以直接調用 |
| import | 用于導入類或者靜態方法 |
| global | 全局變量 |
| function | 自定義函數 |
| query | 查詢 |
| rule end | 規則體 |
?global全局變量
global關鍵字用于在規則文件中定義全局變量,它可以讓應用程序的對象在規則文件中能夠被訪問。可以用來為規則文件提供數據或服務。
語法結構為:global 對象類型 對象名稱
在使用global定義的全局變量時有兩點需要注意:
1、如果對象類型為包裝類型時,在一個規則中改變了global的值,那么只針對當前規則有效,對其他規則中的global不會有影響。可以理解為它是當前規則代碼中的global副本,規則內部修改不會影響全局的使用。
2、如果對象類型為集合類型或JavaBean時,在一個規則中改變了global的值,對java代碼和所有規則都有效。
第一步:編寫規則文件
package rules import com.zzg.model.GlobalEntity/*用于測試Drools 全局變量 : global */global java.lang.Integer globalCount global java.util.List globalListrule "rule_global_1"when$globalEntity:GlobalEntity(num > 1)thenSystem.out.println("規則 rule_global_1 開始...");globalCount++ ;globalList.add("張三");globalList.add("李四");System.out.println(globalCount);System.out.println(globalList);System.out.println("規則 rule_global_1 結束..."); endrule "rule_global_2"when$globalEntity:GlobalEntity(num > 1)thenSystem.out.println("規則 rule_global_2 開始...");System.out.println(globalCount);System.out.println(globalList);System.out.println("規則 rule_global_2 結束..."); end編寫單元測試
// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 GlobalEntity globalEntity = new GlobalEntity(); globalEntity.setNum(20);ArrayList<Object> globalList = new ArrayList<>();Integer globalCount = 10; kieSession.setGlobal("globalCount", 10); kieSession.setGlobal("globalList", globalList);kieSession.insert(globalEntity);// 第五步:執行規則引擎kieSession.fireAllRules();// 第六步:關閉sessionkieSession.dispose();query查詢
query查詢提供了一種查詢working memory中符合約束條件的Fact對象的簡單方法。它僅包含規則文件中的LHS部分,不用指定“when”和“then”部分并且以end結束。
語法結構如下:
query 查詢的名稱(可選參數)LHS end編寫規則文件
package rules import com.zzg.model.QueryEntity/*用于測試Drools 方法: query *///無參查詢 query "query_1"$queryEntity:QueryEntity(age>20) end//有參查詢 query "query_2"(Integer qAge,String qName)$queryEntity:QueryEntity(age > qAge && name == qName) end編寫單元測試
// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 QueryEntity queryEntity1= new QueryEntity(); QueryEntity queryEntity2= new QueryEntity(); QueryEntity queryEntity3= new QueryEntity();queryEntity1.setName("張三").setAge(10); queryEntity2.setName("李四").setAge(20); queryEntity3.setName("王五").setAge(30);kieSession.insert(queryEntity1); kieSession.insert(queryEntity2); kieSession.insert(queryEntity3);QueryResults results1 = kieSession.getQueryResults("query_1"); QueryResults results2 = kieSession.getQueryResults("query_2", 1, "張三");for (QueryResultsRow queryResultsRow : results1) {QueryEntity queryEntity = (QueryEntity) (queryResultsRow.get("$queryEntity"));System.out.println(queryEntity);}for (QueryResultsRow queryResultsRow : results2) {QueryEntity queryEntity = (QueryEntity) (queryResultsRow.get("$queryEntity"));System.out.println(queryEntity);} // 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();?function函數
function關鍵字用于在規則文件中定義函數,就相當于java類中的方法一樣。可以在規則體中調用定義的函數。使用函數的好處是可以將業務邏輯集中放置在一個地方,根據需要可以對函數進行修改。
函數定義的語法結構如下:
function 返回值類型 函數名(可選參數){ //邏輯代碼}編寫規則文件/resources/rules/function.drl
package rules import com.zzg.model.FunctionEntity/*用于測試Drools 方法: function *///定義一個 假發 方法 function Integer add(Integer num){return num+10; }rule "function"when$functionEntity:FunctionEntity(num>20)thenInteger result = add($functionEntity.getNum());System.out.println(result); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 FunctionEntity functionEntity = new FunctionEntity(); functionEntity.setNum(30);kieSession.insert(functionEntity);// 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();條件-LHS加強
前面我們已經知道了在規則體中的LHS部分是介于when和then之間的部分,主要用于模式匹配,只有匹配結果為true時,才會觸發RHS部分的執行。本章節我們會針對LHS部分學習幾個新的用法。
復合值限制in/not in
復合值限制是指超過一種匹配值的限制條件,類似于SQL語句中的in關鍵字。Drools規則體中的LHS部分可以使用in或者not in進行復合值的匹配。
語法結構如下:
Object(field in (比較值1,比較值2...))編寫規則文件
package rules import com.zzg.model.LhsInEntity/*用于測試Drools LHS: in not in */rule "lhs_in"when$lhsInEntity:LhsInEntity(name in ("張三","李四","王五"))thenSystem.out.println("規則 lhs_in 觸發"); endrule "lhs_not_in"when$lhsInEntity:LhsInEntity(name not in ("張三","李四","王五"))thenSystem.out.println("規則 lhs_not_in 觸發"); end條件元素eval
eval用于規則體的LHS部分,并返回一個Boolean類型的值。
語法結構如下:
eval(表達式)編寫規則文件
package rules import com.zzg.model.LhsEvalEntity/*用于測試Drools LHS: in not in */rule "lhs_eval"when$lhsInEntity:LhsEvalEntity(age > 10) and eval(2>1)thenSystem.out.println("規則 lhs_eval 觸發"); end條件元素not
not用于判斷Working Memory中是否存在某個Fact對象,如果不存在則返回true,如果存在則返回false。
語法結構如下:
not Object(可選屬性約束)編寫規則文件
package rules import com.zzg.model.LhsNotEntity/*用于測試Drools LHS: not */rule "lhs_not"whennot $lhsNotEntity:LhsNotEntity(age > 10)thenSystem.out.println("規則 lhs_not 觸發"); end條件元素exists
exists的作用與not相反,用于判斷Working Memory中是否存在某個Fact對象,如果存在則返回true,不存在則返回false。
語法結構如下:
exists Object(可選屬性約束)編寫規則文件
package rules import com.zzg.model.LhsEvalEntity/*用于測試Drools LHS: exists */rule "lhs_exists"whenexists $lhsInEntity:LhsEvalEntity(age > 10)thenSystem.out.println("規則 lhs_eval 觸發"); end疑問:在LHS部分進行條件編寫時并沒有使用exists也可以達到判斷Working Memory中是否存在某個符合條件的Fact元素的目的,那么我們使用exists還有什么意義?
兩者的區別:當向Working Memory中加入多個滿足條件的Fact對象時,使用了exists的規則執行一次,不使用exists的規則會執行多次。
規則繼承
規則之間可以使用extends關鍵字進行規則條件部分的繼承,類似于java類之間的繼承。
結果-RHS
規則文件的RHS部分的主要作用是通過插入,刪除或修改工作內存中的Fact數據,來達到控制規則引擎執行的目的。Drools提供了一些方法可以用來操作工作內存中的數據,操作完成后規則引擎會重新進行相關規則的匹配,原來沒有匹配成功的規則在我們修改數據完成后有可能就會匹配成功了。
insert方法
insert方法的作用是向工作內存中插入數據,并讓相關的規則重新匹配。
編寫規則文件
package rules import com.zzg.model.RhsInsertEntity/*用于測試Drools RHS: insert */rule "rhs_insert_1"when$rhsInsertEntity:RhsInsertEntity(age <= 10)thenRhsInsertEntity rhsInsertEntity = new RhsInsertEntity();rhsInsertEntity.setAge(15);insert(rhsInsertEntity);System.out.println("規則 rhs_insert_1 觸發"); endrule "rhs_insert_2"when$rhsInsertEntity:RhsInsertEntity(age <=20 && age>10)thenRhsInsertEntity rhsInsertEntity = new RhsInsertEntity();rhsInsertEntity.setAge(25);insert(rhsInsertEntity);System.out.println("規則 rhs_insert_2 觸發"); endrule "rhs_insert_3"when$rhsInsertEntity:RhsInsertEntity(age > 20 )thenSystem.out.println("規則 rhs_insert_3 觸發"); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 RhsInsertEntity rhsInsertEntity = new RhsInsertEntity(); rhsInsertEntity.setAge(5);kieSession.insert(rhsInsertEntity);// 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();通過控制臺輸出可以發現,3個規則都觸發了,這是因為首先進行規則匹配時只有第一個規則可以匹配成功,但是在第一個規則中向工作內存中插入了一個數據導致重新進行規則匹配,此時第二個規則可以匹配成功。在第二個規則中同樣向工作內存中插入了一個數據導致重新進行規則匹配,那么第三個規則就出發了。
update方法
update方法的作用是更新工作內存中的數據,并讓相關的規則重新匹配。?(要避免死循環)
編寫規則文件
package rules import com.zzg.model.RhsUpdateEntity/*用于測試Drools RHS: update */rule "rhs_update_1"when$rhsUpdateEntity:RhsUpdateEntity(age <= 10)then$rhsUpdateEntity.setAge(15);update($rhsUpdateEntity);System.out.println("規則 rhs_update_1 觸發"); endrule "rhs_update_2"when$rhsUpdateEntity:RhsUpdateEntity(age <=20 && age>10)then$rhsUpdateEntity.setAge(25);update($rhsUpdateEntity);System.out.println("規則 rhs_update_2 觸發"); endrule "rhs_update_3"when$rhsUpdateEntity:RhsUpdateEntity(age > 20 )thenSystem.out.println("規則 rhs_update_3 觸發"); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 RhsUpdateEntity rhsUpdateEntity = new RhsUpdateEntity(); rhsUpdateEntity.setAge(5);kieSession.insert(rhsUpdateEntity);// 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();通過控制臺的輸出可以看到規則文件中定義的三個規則都觸發了。
在更新數據時需要注意防止發生死循環。
modify方法
modify方法的作用跟update一樣,是更新工作內存中的數據,并讓相關的規則重新匹配。只不過語法略有區別?(要避免死循環)
編寫規則文件
package rules import com.zzg.model.RhsModifyEntity/*用于測試Drools RHS: modify */rule "rhs_modify_1"when$rhsModifyEntity:RhsModifyEntity(age <= 10)thenmodify($rhsModifyEntity){setAge(15)}System.out.println("規則 rhs_modify_1 觸發"); endrule "rhs_modify_2"when$rhsModifyEntity:RhsModifyEntity(age <=20 && age>10)thenmodify($rhsModifyEntity){setAge(25)}System.out.println("規則 rhs_modify_2 觸發"); endrule "rhs_modify_3"when$rhsModifyEntity:RhsModifyEntity(age > 20 )thenSystem.out.println("規則 rhs_modify_3 觸發"); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 RhsModifyEntity rhsModifyEntity = new RhsModifyEntity(); rhsModifyEntity.setAge(5);kieSession.insert(rhsModifyEntity);// 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();通過控制臺的輸出可以看到規則文件中定義的三個規則都觸發了。
在更新數據時需要注意防止發生死循環。
retract/delete方法
retract方法的作用是刪除工作內存中的數據,并讓相關的規則重新匹配。
編寫規則文件
package rules import com.zzg.model.RhsRetractEntity/*用于測試Drools RHS: retract */rule "rhs_retract_1"when$rhsRetractEntity:RhsRetractEntity(age <= 10)then // retract($rhsRetractEntity);System.out.println("規則 rhs_retract_1 觸發"); endrule "rhs_retract_2"when$rhsRetractEntity:RhsRetractEntity(age <= 10)thenSystem.out.println("規則 rhs_retract_2 觸發"); end編寫單元測試
// 第一步 KieServices kieServices = KieServices.Factory.get(); // 第二步 KieContainer kieContainer = kieServices.getKieClasspathContainer(); // 第三步 KieSession kieSession = kieContainer.newKieSession(); // 業務對象 RhsRetractEntity rhsRetractEntity = new RhsRetractEntity(); rhsRetractEntity.setAge(5);kieSession.insert(rhsRetractEntity); // 第五步:執行規則引擎 kieSession.fireAllRules(); // 第六步:關閉session kieSession.dispose();通過控制臺輸出可以發現,只有第一個規則觸發了,因為在第一個規則中將工作內存中的數據刪除了導致第二個規則并沒有匹配成功。
RHS加強
RHS部分是規則體的重要組成部分,當LHS部分的條件匹配成功后,對應的RHS部分就會觸發執行。一般在RHS部分中需要進行業務處理。
在RHS部分Drools為我們提供了一個內置對象,名稱就是drools。本小節我們來介紹幾個drools對象提供的方法。
halt
halt方法的作用是立即終止后面所有規則的執行。
編寫規則文件
package rules import com.zzg.model.RhsHaftEntity/*用于測試Drools RHS: haft */rule "rhs_haft_1"when$rhsHaftEntity:RhsHaftEntity(age <= 10)thendrools.halt();System.out.println("規則 rhs_haft_1 觸發"); endrule "rhs_haft_2"when$rhsHaftEntity:RhsHaftEntity(age <= 20)thenSystem.out.println("規則 rhs_haft_2 觸發"); endgetWorkingMemory
getWorkingMemory方法的作用是返回工作內存對象。
rule "rhs_get_working_memory_1"when$rhsDroolsMethodsEntity:RhsDroolsMethodsEntity(age <= 10)thenSystem.out.println(drools.getWorkingMemory());System.out.println("規則 rhs_get_working_memory_1 觸發"); endgetRule
getRule方法的作用是返回規則對象。
rule "rhs_rule_2"when$rhsDroolsMethodsEntity:RhsDroolsMethodsEntity(age <=20)thenSystem.out.println(drools.getRule());System.out.println("規則 rhs_rule_2 觸發"); end規則文件編碼規范
我們在進行drl類型的規則文件編寫時盡量遵循如下規范:
- 所有的規則文件(.drl)應統一放在一個規定的文件夾中,如:/rules文件夾
- 書寫的每個規則應盡量加上注釋。注釋要清晰明了,言簡意賅
- 同一類型的對象盡量放在一個規則文件中,如所有Student類型的對象盡量放在一個規則文件中
- 規則結果部分(RHS)盡量不要有條件語句,如if(...),盡量不要有復雜的邏輯和深層次的嵌套語句
- 每個規則最好都加上salience屬性,明確執行順序
- Drools默認dialect為"Java",盡量避免使用dialect "mvel"
七、WorkBench
?WorkBench簡介
WorkBench是KIE組件中的元素,也稱為KIE-WB,是Drools-WB與JBPM-WB的結合體。它是一個可視化的規則編輯器。WorkBench其實就是一個war包。
WorkBench經過幾次版本迭代,已經不提供tomcat啟動的war包,綜合考慮,本課程仍然采用 tomcat版本作為演示。
環境:
- apache-tomcat-9.0.29
- kie-drools-wb-7.6.0.Final-tomcat8 下載地址:Drools - Download
說明:
準備jar包:需要放到tomcat lib中,否則啟動失敗
具體安裝步驟:
配置 Tomcat
1.修改tomcat-user.xml,添加用戶
<?xml version="1.0" encoding="UTF-8"?> <tomcat-users xmlns="http://tomcat.apache.org/xml"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"version="1.0"><!--定義admin角色--><role rolename="admin"/><!--定義一個用戶,用戶名為kie,密碼為kie,對應的角色為admin角色--><user username="kie-web" password="kie-web123" roles="admin"/><user username="admin" password="admin" roles="manager-gui,manager-script,manager-jmx,manager-status"/> </tomcat-users>2.修改server.xml
<Host name="localhost" appBase="webapps"unpackWARs="true" autoDeploy="true"><!-- SingleSignOn valve, share authentication between web applicationsDocumentation at: /docs/config/valve.html --><!--<Valve className="org.apache.catalina.authenticator.SingleSignOn" />--><!-- Access log processes all example.Documentation at: /docs/config/valve.htmlNote: The pattern used is equivalent to using pattern="common" --><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"prefix="localhost_access_log" suffix=".txt"pattern="%h %l %u %t "%r" %s %b" /><Valve className="org.kie.integration.tomcat.JACCValve"/></Host>3.復制jar到tomcat根目錄的lib下面:
kie-tomcat-integration-7.10.0.Final.jar javax.security.jacc-api-1.5.jar slf4j-api-1.7.25.jar4.復制 kie-drools-wb-7.6.0.Final-tomcat8.war 到tomcat webapp下面并修改成kie-web.war
啟動服務器
啟動tomcat
訪問http://localhost:8080/kie-web,可以看到WorkBench的登錄頁面。使用前面創建的kie-web/kie-web123登錄
登錄成功后進入系統首頁:
WorkBench使用?
創建空間、項目
首頁中點擊 project,創建空間
?
我們創建一個 ityml 的工作空間。點擊 Save,保存。
?點擊工作空間當中的 ityml,進入空間
點擊Add Project添加項目
成功后,我們可以看見下圖
?左上角的導航條,可以在空間和project之間切換
創建數據對象和drl文件
切換到pro1項目內,點擊 Create New Assert
選中數據對象:
?輸入Order,點擊確定,成功后跳轉如下頁面
?
Order相當于我們代碼中的實體類,在左側 Project Explorer視圖中,可以看見項目結構
接下來添加字段,點擊添加字段按鈕:
?ID 位置,輸入java bean的字段,標簽是備注信息,類型選擇對應的字段類型,保存,點擊創建,關閉彈窗,點擊創建并繼續,可以繼續創建。
點擊右上角的保存,至此,一個數據對象我們就創建完成,可以在源代碼中查看代碼內容。
接下來我們創建一個drl文件,創建過程跟創建bean類似,drl文件內容如下
package com.ityml.pro1;rule "rule_1"when$order:Order(age > 10)thenSystem.out.print("rule run..."); end保存之后,點擊導航條回到項目主頁
設置KieBase+KieSession
項目首頁點擊Settings
選擇知識庫跟會話
?
彈出窗口,輸入Kiebase名稱即可,我們以kb1為例
?同理,我們補充完軟件包信息,添加只是會話,即kiesession
?操作完成后,不要忘記保存,此時,我們可在Project Explorer視圖中,resource/META-INF/kmodule.xml中看見如下信息
<kmodule xmlns="http://www.drools.org/xsd/kmodule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><kbase name="kb1" default="false" eventProcessingMode="stream" equalsBehavior="identity" packages="com.ityml.pro1"><ksession name="ks1" type="stateful" default="true" clockType="realtime"/></kbase> </kmodule>導航回到項目首頁,進行編譯發布
發布成功后,我們可以在maven倉庫中看到對應的jar
?
也可以訪問:http://localhost:8080/kie-web/maven2/com/ityml/pro1/1.0.0/pro1-1.0.0.jar?驗證是否發布成功.
代碼使用?
@Testpublic void test() throws Exception{//通過此URL可以訪問到maven倉庫中的jar包//URL地址構成:http://ip地址:Tomcat端口號/WorkBench工程名/maven2/坐標/版本號/xxx.jarString url = "http://localhost:8080/kie-web/maven2/com/ityml/pro1/1.0.0/pro1-1.0.0.jar";KieServices kieServices = KieServices.Factory.get();UrlResource resource = (UrlResource) kieServices.getResources().newUrlResource(url);//認證resource.setUsername("kie-web");resource.setPassword("kie-web123");resource.setBasicAuthentication("enabled");KieRepository repository = kieServices.getRepository();//通過輸入流讀取maven倉庫中的jar包數據,包裝成KieModule模塊添加到倉庫中KieModule kieModule = repository.addKieModule(kieServices.getResources().newInputStreamResource(resource.getInputStream()));KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());KieSession session = kieContainer.newKieSession();Order order = new Order();order.setName("張三");order.setAge(30);session.insert(order);session.fireAllRules();session.dispose();}本文參考資料:
Drools規則引擎?
總結
以上是生活随笔為你收集整理的Drools 规则引擎一文读懂的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTML5游戏引擎Playcraft将于
- 下一篇: vux 地图插件_基于vue的移动端组件