文章結構
1. 基礎api
2. FACT對象
3. 規則
4. 函數
在 Drools 當中,規則的編譯與運作要通過Drools 提供的各種API 來實作,這些API 總體來講可以分為三類:規則編譯、規則收集和規則的執行。完成這些工作的API 主要有KnowledgeBuilder、KnowledgeBase、StatefulKnowledgeSession、StatelessKnowledgeSession、、等,它們起到了對規則檔案進行收集、編譯、查錯、插入fact、設定global、執行規則或規則流等作用
1.1 KnowledgeBuilder
KnowledgeBuilder 在業務代碼當中收集已經編寫好的規則, 然後對這些規則檔案進行編譯, 最終産生一批編譯好的規則包(KnowledgePackage)給其它的應用程式使用。KnowledgeBuilder 在編譯規則的時候可以通過其提供的hasErrors()方法得到編譯規則過程中發現規則是否有錯誤,如果有的話通過其提供的getErrors()方法将錯誤列印出來,以幫助我們找到規則當中的錯誤資訊。
1.2 KnowledgeBase
KnowledgeBase 是 Drools 提供的用來收集應用當中知識(knowledge)定義的知識庫對象,在一個KnowledgeBase 當中可以包含普通的規則(rule)、規則流(rule flow)、函數定義(function)、使用者自定義對象(type model)等。KnowledgeBase 本身不包含任何業務資料對象(fact 對象,後面有相應章節着重介紹fact 對象),業務對象都是插入到由KnowledgeBase産生的兩種類型的session 對象當中(StatefulKnowledgeSession 和StatelessKnowledgeSession,後面會有對應的章節對這兩種類型的對象進行介紹),通過session 對象可以觸發規則執行或開始一個規則流執行。
1.3. StatefulKnowledgeSessions
StatefulKnowledgeSession 對象是一種最常用的與規則引擎進行互動的方式,它可以與規則引擎建立一個持續的互動通道,在推理計算的過程當中可能會多次觸發同一資料集。在使用者的代碼當中,最後使用完StatefulKnowledgeSession 對象之後,一定要調用其dispose()方法以釋放相關記憶體資源。
1.4. StateLessKnowledgeSession
StatelessKnowledgeSession 的作用與StatefulKnowledgeSession 相仿,它們都是用來接收業務資料、執行規則的。事實上,StatelessKnowledgeSession 對StatefulKnowledgeSession 做了包裝,使得在使用StatelessKnowledgeSession 對象時不需要再調用dispose()方法釋放記憶體資源了。
在drools 6.x以後這些API 都整合到kie API 中了
Fact 是指在Drools 規則應用當中,将一個普通的JavaBean 插入到規則的WorkingMemory當中後的對象。規則可以對Fact 對象進行任意的讀寫操作,當一個JavaBean 插入到WorkingMemory 當中變成Fact 之後,Fact 對象不是對原來的JavaBean 對象進行Clon,而是原來JavaBean 對象的引用。
3.1規則檔案
在 Drools 當中,一個标準的規則檔案就是一個以“.drl”結尾的文本檔案,由于它是一個标準的文本檔案,是以可以通過一些記事本工具對其進行打開、檢視和編輯。
規則是放在規則檔案當中的,一個規則檔案可以存放多個規則,除此之外,在規則檔案當中還可以存放使用者自定義的函數、資料對象及自定義查詢等相關在規則當中可能會用到的一些對象。常用的有:package package-name、imports、globals、functions、queries、rules
對于一個規則檔案而言,首先聲明package 是必須的,除package 之外,其它對象在規則檔案中的順序是任意的,也就是說在規則檔案當中必須要有一個package 聲明,同時package 聲明必須要放在規則檔案的第一行。在Drools 的規則檔案當中package 對于規則檔案中規則的管理隻限于邏輯上的管理,,而不管其在實體上的位置如何,這點是規則與Java 檔案的package 的差別。對于同一package 下的使用者自定義函數、自定義的查詢等,不管這些函數與查詢是否在同一個規則檔案裡面,在規則裡面是可以直接使用的,這點和Java 的同一package 裡的Java類調用是一樣的。
3.2 規則語言
rule "name"
attributes
when
LHS
Then
RHS
end
一個規則通常包括三個部分:屬性部分(attribute)、條件部分(LHS)和結果部分(RHS)。對于一個完整的規則來說,這三個部分都是可選的,也就是說如下 所示的規則是合法的:
rule "name"
when
then
end
3.3 條件部分
條件部分又被稱之為Left Hand Side,簡稱為LHS,下文當中,如果沒有特别指出,那麼所說的LHS 均指規則的條件部分,在一個規則當中when 與then 中間的部分就是LHS 部分。在LHS 當中,可以包含0~n 個條件,如果LHS 部分沒空的話,那麼引擎會自動添加一個eval(true)的條件,由于該條件總是傳回true,是以LHS 為空的規則總是傳回true。LHS 部分是由一個或多個條件組成,條件又稱之為pattern(比對模式),多個pattern之間用可以使用and 或or 來進行連接配接,同時還可以使用小括号來确定pattern 的優先級。
一個pattern 的文法如下:
[綁定變量名:]Object([field 限制])
對于一個pattern 來說“綁定變量名”是可選的,如果在目前規則的LHS 部分的其它的pattern 要用到這個對象,那麼可以通過為該對象設定一個綁定變量名來實作對其引用,對于綁定變量的命名,通常的作法是為其添加一個“$”符号作為字首,這樣可以很好的與Fact的屬性差別開來;綁定變量不僅可以用在對象上,也可以用在對象的屬性上面,命名方法與對象的命名方法相同;“field 限制”是指目前對象裡相關字段的條件限制,示例如下:
rule "rule1"
when
$customer:Customer(age>20,gender==’male’)
Order(customer==$customer,price>1000)
then
<action>…
End
此段規則的含義為:的規則就包含兩個pattern,第一個pattern 有三個限制,分别是:對象類型必須是Cutomer;同時Cutomer 的age 要大于20 且gender 要是male;第二個pattern 也有三個限制,分别是:對象類型必須是Order,同時Order 對應的Cutomer 必須是前面的那個Customer 且目前這個Order 的price 要大于1000。在這兩個pattern 沒有符号連接配接,在Drools當中在pattern 中沒有連接配接符号,那麼就用and 來作為預設連接配接,是以在該規則的LHS 部分中兩個pattern 隻有都滿足了才會傳回true。預設情況下,每行可以用“;”來作為結束符(和Java 的結束一樣),當然行尾也可以不加“;”結尾。
3.3.1限制連接配接
對于對象内部的多個限制的連接配接,可以采用“&&”(and)、“||”(or)和“,”(and)來實作,“&&”(and)、“||”(or)和“,”這三個連接配接符号如果沒有用小括号來顯示的定義優先級的話,那麼它們的執行順序是:“&&”(and)、“||”(or)和“,” “&&”優先級最高,表面上看“,”與“&&”具有相同的含義,但是有一點需要注意,“,”與“&&”和“||”不能混合使用,也就是說在有“&&”或“||”出現的LHS 當中,是不可以有“,”連接配接符出
現的,反之亦然。
3.3.2 比較操作符
在當中共提供了十二種類型的比較操作符,分别是:>、>=、<、<=、= =、!=、contains、not contains、memberof、not memberof、matches、not matches;在這十二種類型的比較操作符當中,前六個是比較常見也是用的比較多的比較操作符,着重對後六種類型的比較操作符進行介紹。
3.3.2.1 contains
比較操作符contains 是用來檢查一個Fact 對象的某個字段(該字段要是一個Collection或是一個Array 類型的對象)是否包含一個指定的對象。
when
$order:Order();
$customer:Customer(age >20, orders contains $order);
then
System.out.println($customer.getName());
End
contains 隻能用于對象的某個Collection/Array 類型的字段與另外一個值進行比較,作為比較的值可以是一個靜态的值,也可以是一個變量(綁定變量或者是一個global 對象)。
3.3.2.2 not contains
not contains 作用與contains 作用相反,not contains 是用來判斷一個Fact 對象的某個字段(Collection/Array 類型)是不是不包含一個指定的對象,和contains 比較符相同,它也隻能用在對象的field 當中。
3.3.2.3 memberOf
memberOf 是用來判斷某個Fact 對象的某個字段是否在一個集合(Collection/Array)當中,用法與contains 有些類似,但也有不同,memberOf 的文法如下:Object(fieldName memberOf value[Collection/Array])可以看到memberOf 中集合類型的資料是作為被比較項的,集合類型的資料對象位于memberOf 操作符後面,同時在用memberOf 比較操作符時被比較項一定要是一個變量(綁定變量或者是一個global 對象),而不能是一個靜态值。
3.3.2.4 not memberOf
該操作符與memberOf 作用洽洽相反,是用來判斷Fact 對象當中某個字段值是不是中某個集合(Collection/Array)當中,同時被比較的集合對象隻能是一個變量(綁定變量或global對象)。
3.3.2.5 matches
matches 是用來對某個Fact 的字段與标準的Java 正規表達式進行相似比對,被比較的字元串可以是一個标準的Java 正規表達式,但有一點需要注意,那就是正規表達式字元串當中不用考慮“\”的轉義問題
when
$customer:Customer(name matches "李.*");
then
System.out.println($customer.getName());
end
該規則是用來查找所有Customer 對象的name 屬性是不是以“李”字開頭,如果滿足這一條件那麼就将該Customer 對象的name 屬性列印出來。
3.3.2.6 not matches
與matches 作用相反,是用來将某個Fact 的字段與一個Java 标準正規表達式進行比對,看是不是能與正規表達式比對。not matches 使用文法如下:
Object(fieldname not matches “正規表達式”)
3.4 結果部分
結果部分又被稱之為Right Hand Side,簡稱為RHS,在一個規則當中then 後面部分就是RHS,隻有在LHS 的所有條件都滿足時RHS 部分才會執行。
RHS 部分是規則真正要做事情的部分,可以将因條件滿足而要觸發的動作寫在該部分當中,在RHS 當中可以使用LHS 部分當中定義的綁定變量名、設定的全局變量、或者是直接編寫Java 代碼(對于要用到的Java 類,需要在規則檔案當中用import 将類導入後方能使用,這點和Java 檔案的編寫規則相同)。
在規則當中LHS 就是用來放置條件的,是以在RHS 當中雖然可以直接編寫Java 代碼,但不建議在代碼當中有條件判斷,如果需要條件判斷,那麼請重新考慮将其放在LHS 當中,否則就違背了使用規則的初衷。
在 Drools 當中,在RHS 裡面,提供了一些對目前Working Memory 實作快速操作的宏函數或宏對象,比如insert/insertLogical、update 和retract 就可以實作對目前Working Memory中的Fact 對象進行新增、删除或者是修改.
3.4.1 insert
insert(new Object());
一旦調用insert 宏函數,那麼Drools 會重新與所有的規則再重新比對一次,對于沒有設定no-loop 屬性為true 的規則,如果條件滿足,不管其之前是否執行過都會再執行一次,這個特性不僅存在于insert 宏函數上,後面介紹的update、retract 宏函數同樣具有該特性,是以在某些情況下因考慮不周調用insert、update 或retract容易發生死循環,這點大家需要注意.
when
eval(true);
then
Customer cus=new Customer();
cus.setName("張三");
insert(cus);
end
3.4.2 update
update 函數意義與其名稱一樣,用來實作對目前Working Memory 當中的Fact 進行更新,update 宏函數的作用與StatefulSession 對象的update 方法的作用基本相同,都是用來告訴目前的Working Memory 該Fact 對象已經發生了變化。
when
$customer:Customer(name=="張三",age<10);
then
$customer.setAge($customer.getAge()+1);
update($customer);
System.out.println("----------"+$customer.getName());
End
3.4.3 retract
retract 用來将Working Memory 當中某個Fact 對象從Working Memory 當中删除,下面就通過一個例子來說明retract 宏函數的用法。
retract($customer);
3.4.4 modify
modify 是一個表達式塊,它可以快速實作對Fact 對象多個屬性進行修改,修改完成後會自動更新到目前的Working Memory 當中。它的基本文法格式如下:
modify(fact-expression){
<修改Fact 屬性的表達式>[,<修改Fact 屬性的表達式>*]
}
示例
when
$customer:Customer(name=="張三",age==20);
then
System.out.println("modify before customer
id:"+$customer.getId()+";age:"+$customer.getAge());
modify($customer){
setId("super man"),
setAge(30)
}
End
這裡有一點需要注意,那就是和insert、update、retract 對Working Memory 的操作一樣,一旦使用了modify 塊對某個Fact 的屬性進行了修改,那麼會導緻引擎重新檢查所有規則是否比對條件,而不管其之前是否執行過。
3.5 屬性部分
規則屬性是用來控制規則執行的重要工具,在規則的屬性共有13 個,它們分别是:activation-group、agenda-group、
auto-focus、date-effective、date-expires、dialect、duration、enabled、lock-on-active、no-loop、ruleflow-group、salience、when,這些屬性分别适用于不同的場景,下面我們就來分别介紹這些屬性的含義及用法。
3.5.1 salience
用來設定規則執行的優先級,salience 屬性的值是一個數字,數字越大執行優先級越高,同時它的值可以是一個負數。預設情況下,規則的ssalience 預設值為0,是以如果我們不手動設定規則的salience 屬性,那麼它的執行順序是随機(但是一般都是按照加載順序。)
rule "rule1"
salience 1
when
eval(true)
then
System.out.println("rule1");
End
3.5.2 no-loop
no-loop 屬性的作用是用來控制已經執行過的規則在條件再次滿足時是否再次執行。預設情況下規則的no-loop屬性的值為false,如果no-loop 屬性值為true,那麼就表示該規則隻會被引擎檢查一次,
no-loop 是 對于自己規則的操作引起重新比對,隻執行一次。由于其他規則的操作引起的重新比對會執行一次。是以如果有兩個
規則都是一直滿足條件,且then 中有update 操作(如下例子)。那麼将會進入死循環,此時應該使用 lock-on-active 屬性保證隻比對一次。
rule "rule1"
salience 1
no-loop true
when
$customer:Customer(name=="張三")
then
update($customer);
System.out.println("customer name:"+$customer.getName());
End
3.5.3 date-effective
控制規則隻有在到達後才會觸發。隻有當系統時間>=date-effective 設定的時間值時,規則才會觸發執行,否則執行将不執行。在沒有設定該屬性的情況下,規則随時可以觸發,沒有這種限制。
date-effective 可接受的日期格式為“dd-MMM-yyyy”,例如2009 年9 月25 日
如果您的作業系統為中文的,那麼應該寫成“25-Sep-2009”;如果是英文作業系統“25-九月-2009”
rule "rule1"
date-effective " 25-九月-2009"
when
eval(true);
then
System.out.println("rule1 is execution!");
End
3.5.4 date-expires
該屬性的作用與date-effective 屬性恰恰相反, date-expires 的作用是用來設定規則的有效期。如果date-expires 的值大于系統時間,那麼規則就執行,否則就不執行。
具體用法與date-effective 屬性相同。
3.5.5 enabled
enabled 屬性比較簡單,它是用來定義一個規則是否可用的。該屬性的值是一個布爾值,預設該屬性的值為true,表示規則是可用的,如果手工為一個規則添加一個enabled 屬性,并且設定其enabled 屬性值為false,那麼引擎就不會執行該規則。
3.5.6 dialect
該屬性用來定義規則當中要使用的語言類型,目前Drools 版本當中支援兩種類型的語言:mvel 和java,預設情況下,如果沒有手工設定規則的dialect,那麼使用的java 語言。
3.5.7 duration
如果設定了該屬性,那麼規則将在該屬性指定的值之後在另外一個線程裡觸發。該屬性對應的值為一個長整型,機關是毫秒.
rule "rule1"
duration 3000
when
eval(true)
then
System.out.println("rule thread
id:"+Thread.currentThread().getId());
end
表示該規則将在3000 毫秒之後在另外一個線程裡觸發。
5.8 lock-on-active
确認規則隻執行一次。 将lock-on-action 屬性的值設定為true,可能避免因某些Fact 對象被修改而使已經執行過的規則再次被激活執行。lock-on-active 是no-loop 的增強版屬性。lock-on-active 屬性預設值為false。
5.9 activation-group
該屬性的作用是将若幹個規則劃分成一個組,用一個字元串來給這個組命名,這樣在執行的時候,具有相同activation-group 屬性的規則中隻要有一個會被執行,其它的規則都将不再執行。
在一組具有相同activation-group 屬性的規則當中,隻有一個規則會被執行,其它規則都将不會被執行。當然對于具有相同activation-group 屬性的規則當中究竟哪一個會先執行,則可以用類似salience 之類屬性來實作。
rule "rule1"
activation-group "test"
when
eval(true)
then
System.out.println("rule1 execute");
end
rule "rule 2"
activation-group "test"
when
eval(true)
then
System.out.println("rule2 execute");
End
rule1 和rule2 這兩個規則因為具體相同名稱的activation-group 屬性,是以它們隻有一個會被執行。
3.5.10 agenda-group
Agenda Group 是用來在Agenda 的基礎之上,對現在的規則進行再次分組,具體的分組方法可以采用為規則添加agenda-group 屬性來實作。
agenda-group 屬性的值也是一個字元串,通過這個字元串,可以将規則分為若幹個Agenda Group,預設情況下,引擎在調用這些設定了agenda-group 屬性的規則的時候需要顯示的指定某個Agenda Group 得到Focus(焦點),這樣位于該Agenda Group 當中的規則才會觸發執行,否則将不執行。
rule "rule1"
agenda-group "001"
when
eval(true)
then
System.out.println("rule1 execute");
end
rule "rule 2"
agenda-group "002"
when
eval(true)
then
System.out.println("rule2 execute");
End
java 代碼
//getSession 擷取KieSession 的方法自己寫的。
KieSession ks = getSession();
//設定agenda-group 的auto-focus 使其執行 ks.getAgenda().getAgendaGroup("group1").setFocus();
3.5.11 auto-focus
前面我們也提到auto-focus 屬性,它的作用是用來在已設定了agenda-group 的規則上設定該規則是否可以自動獨取Focus,如果該屬性設定為true,那麼在引擎執行時,就不需要顯示的為某個Agenda Group 設定Focus,否則需要。
對于規則的執行的控制,還可以使用Agenda Filter 來實作。在Drools 當中,提供了一個名為org.drools.runtime.rule.AgendaFilter 的Agenda Filter 接口,使用者可以實作該接口,通過規則當中的某些屬性來控制規則要不要執行。org.drools.runtime.rule.AgendaFilter 接口隻有一個方法需要實作,方法體如下:
public boolean accept(Activation activation);
在該方法當中提供了一個Activation 參數,通過該參數我們可以得到目前正在執行的規則對象或其它一些屬性,該方法要傳回一個布爾值,該布爾值就決定了要不要執行目前這個規則,傳回true 就執行規則,否則就不執行。
在引擎執行規則的時候,我們希望使用規則名來對要執行的規則做一個過濾,此時就可以通過AgendaFilter 來實作,示例代碼既為我們實作的一個AgendaFilter 類源碼。
import org.drools.runtime.rule.Activation;
import org.drools.runtime.rule.AgendaFilter;
public class TestAgendaFilter implements AgendaFilter {
private String startName;
public TestAgendaFilter(String startName){
this.startName=startName;
}
public boolean accept(Activation activation) {
String ruleName=activation.getRule().getName();
if(ruleName.startsWith(this.startName)){
return true;
}else{
return false;
}
}
}
從實作類中可以看到,我們采用的過濾方法是規則名的字首,通過Activation 得到目前的Rule 對象,然後得到目前規則的name,再用這個name 與給定的name 字首進行比較,如果相同就傳回true,否則就傳回false。
java 中調用
TestAgendaFilter filter= new TestAgendaFilter("activa");
int count = ks.fireAllRules(filter);
3.5.12 ruleflow-group
在使用規則流的時候要用到ruleflow-group 屬性,該屬性的值為一個字元串,作用是用來将規則劃分為一個個的組,然後在規則流當中通過使用ruleflow-group 屬性的值,進而使用對應的規則。
函數是定義在規則檔案當中一代碼塊,作用是将在規則檔案當中若幹個規則都會用到的業務操作封裝起來,實作業務代碼的複用,減少規則編寫的工作量。函數的編寫位置可以是規則檔案當中package 聲明後的任何地方,Drools 當中函數聲明。
function void/Object functionName(Type arg...) {
/*函數體的業務代碼*/
}
Drools 當中的函數以function 标記開頭,如果函數體沒有傳回值,那麼function 後面就是void,如果有傳回值這裡的void 要換成對應的傳回值對象,接下來就是函數的名稱函數名稱的定義可以參考Java 類當中方法的命名原則,對于一個函數可以有若幹個輸入參數,是以函數名後面的括号當中可以定義若幹個輸入參數。
Drools 為我們提供了一個特殊的import 語句:import function,通過該import語句,可以實作将一個Java 類中靜态方法引入到一個規則檔案當中,使得該檔案當中的規則可以像使用普通的Drools 函數一樣來使用Java 類中某個靜态方法。
import function test.RuleTools.printInfo;
通過使用import function 關鍵字,将test.RuleTools 類中靜态方法printInfo 引入到目前規則檔案中。
調用方式
RuleTools.printInfo(...)
原文位址:https://blog.csdn.net/u012373815/article/details/53872025