對于Drools Rule的理解
一個規(guī)則可以包含三個部分:
- 屬性部分:定義當前規(guī)則執(zhí)行的一些屬性等,比如是否可被重復執(zhí)行膏萧、過期時間、生效時間等。
- 條件部分(LHS):定義當前規(guī)則的條件丸冕,如 when Message(); 判斷當前workingMemory中是否存在Message對象。
- 結果部分(RHS):這里可以寫普通java代碼薛窥,即當前規(guī)則條件滿足后執(zhí)行的操作胖烛,可以直接調(diào)用Fact對象的方法來操作應用。
屬性部分:
規(guī)則的屬性有以下幾種
activation-group诅迷、agenda-group佩番、auto-focus、date-effective罢杉、date-expires趟畏、dialect、duration屑那、duration-value拱镐、enabled、lock-on-active持际、no-loop、ruleflow-group哗咆、salience
幾個重要的屬性:
- activation-group
該屬性的作用是將若干個規(guī)則劃分成一個組蜘欲,用一個字符串來給這個組命名,這樣在執(zhí)行的時候晌柬,具有相同activation-group 屬性的規(guī)則中只要有一個會被執(zhí)行姥份,其它的規(guī)則都將不再執(zhí)行。也就是說年碘,在一組具有相同activation-group 屬性的規(guī)則當中澈歉,只有一個規(guī)則會被執(zhí)行,其它規(guī)則都將不會被執(zhí)行屿衅。
- auto-focus
用來在已設置了agenda-group 的規(guī)則上設置該規(guī)則是否可以自動獨取Focus埃难,如果該屬性設置為true,那么在引擎執(zhí)行時,就不需要顯示的為某個Agenda Group 設置Focus涡尘,否則需要忍弛。對于規(guī)則的執(zhí)行的控制,還可以使用Agenda Filter 來實現(xiàn)考抄。在Drools 當中细疚,提供了一個名為org.drools.runtime.rule.AgendaFilter 的Agenda Filter 接口,用戶可以實現(xiàn)該接口川梅,通過規(guī)則當中的某些屬性來控制規(guī)則要不要執(zhí)行疯兼。org.drools.runtime.rule.AgendaFilter 接口只有一個方法需要實現(xiàn),方法體如下: public boolean accept(Activation activation); 在該方法當中提供了一個Activation 參數(shù)贫途,通過該參數(shù)我們可以得到當前正在執(zhí)行的規(guī)則對象或其它一些屬性镇防,該方法要返回一個布爾值,該布爾值就決定了要不要執(zhí)行當前這個規(guī)則潮饱,返回true 就執(zhí)行規(guī)則来氧,否則就不執(zhí)行
與agenda-group配合使用,設置焦點的是否可以自動獲取boolean型香拉,默認值為false
- agenda-group
Agenda Group 是用來在Agenda 的基礎之上啦扬,對現(xiàn)在的規(guī)則進行再次分組,具體的分組方法可以采用為規(guī)則添加agenda-group 屬性來實現(xiàn)凫碌。agenda-group 屬性的值也是一個字符串扑毡,通過這個字符串,可以將規(guī)則分為若干個Agenda Group盛险,默認情況下瞄摊,引擎在調(diào)用這些設置了agenda-group 屬性的規(guī)則的時候需要顯示的指定某個Agenda Group 得到Focus(焦點),這樣位于該Agenda Group 當中的規(guī)則才會觸發(fā)執(zhí)行苦掘,否則將不執(zhí)行
基于Agenda將規(guī)則分組换帜;只有當某個Agenda組獲取到焦點(focus)時,該組的規(guī)則才會被執(zhí)行string型鹤啡,默認值為MAIN規(guī)則的調(diào)用與執(zhí)行是通過StatelessSession 或StatefulSession 來實現(xiàn)的惯驼,一般的順序是創(chuàng)建一個StatelessSession 或StatefulSession,將各種經(jīng)過編譯的規(guī)則的package 添加到session當中递瑰,接下來將規(guī)則當中可能用到的Global 對象和Fact 對象插入到Session 當中祟牲,最后調(diào)用fireAllRules 方法來觸發(fā)、執(zhí)行規(guī)則抖部。在沒有調(diào)用最后一步fireAllRules 方法之前说贝,所有的規(guī)則及插入的Fact 對象都存放在一個名叫Agenda 表的對象當中,這個Agenda 表中每一個規(guī)則及與其匹配相關業(yè)務數(shù)據(jù)叫做Activation慎颗,在調(diào)用fireAllRules 方法后乡恕,這些Activation 會依次執(zhí)行言询,這些位于Agenda 表中的Activation 的執(zhí)行順序在沒有設置相關用來控制順序的屬性時(比如salience 屬性),它的執(zhí)行順序是隨機的几颜,不確定的倍试。
- no-loop
drools提供了一些api,可以對當前傳入workingMemory中的Fact對象進行修改或者個數(shù)的增減蛋哭,比如上述的update方法县习,就是將當前的workingMemory中的Message類型的Fact對象進行屬性更新,這種操作會觸發(fā)規(guī)則的重新匹配執(zhí)行谆趾,可以理解為Fact對象更新了躁愿,所以規(guī)則需要重新匹配一遍。update之后沪蓬,之前的修改都會生效彤钟。當然對Fact對象數(shù)據(jù)的修改并不是一定需要調(diào)用update才可以生效,簡單的使用set方法設置就可以完成跷叉,這里類似于java的引用調(diào)用逸雹,所以何時使用update是一個需要仔細考慮的問題,一旦不慎云挟,極有可能會造成規(guī)則的死循環(huán)梆砸。上述的no-loop true,即設置當前的規(guī)則园欣,只執(zhí)行一次帖世,如果本身的RHS部分有update等觸發(fā)規(guī)則重新執(zhí)行的操作,也不要再次執(zhí)行當前規(guī)則沸枯。
- lock-on-active
當在規(guī)則上使用ruleflow-group屬性或agenda-group屬性的時候日矫,將lock-on-active 屬性的值設置為true,可避免因某些Fact對象被修改而使已經(jīng)執(zhí)行過的規(guī)則再次被激活執(zhí)行绑榴∧慕危可以看出該屬性與no-loop屬性有相似之處,no-loop屬性是為了避免Fact被修改或調(diào)用了insert彭沼、retract缔逛、update之類的方法而導致本規(guī)則再次激活執(zhí)行,這里的lock-on-active 屬性起同樣的作用姓惑,lock-on-active是no-loop的增強版屬性,它主要作用在使用ruleflow-group屬性或agenda-group屬性的時候按脚。lock-on-active屬性默認值為false于毙。與no-loop不同的是lock-on-active可以避免其他規(guī)則修改FACT對象導致規(guī)則的重新執(zhí)行。
- date-effective辅搬、date-expires唯沮、enabled
顧名思義
- salience
它的作用是用來設置規(guī)則執(zhí)行的優(yōu)先級脖旱,salience 屬性的值是一個數(shù)字,數(shù)字越大執(zhí)行優(yōu)先級越高介蛉,同時它的值可以是一個負數(shù)萌庆。默認情況下,規(guī)則的salience 默認值為0币旧,所以如果我們不手動設置規(guī)則的salience 屬性践险,那么它的執(zhí)行順序是隨機的
int型,默認值為0
- rule-flow-group
基于ruleflow將規(guī)則分組string型吹菱,無默認值巍虫,作用是用來將規(guī)則劃分為一個個的組,然后在規(guī)則流當中通過使用ruleflow-group 屬性的值鳍刷,從而使用對應的規(guī)則
作用等同于agenda-group占遥。二選一即可
- dialect:
該屬性用來定義規(guī)則當中要使用的語言類型,目前Drools支持兩種語言:mvel 和java输瓜,默認使用的java 語言
- duration:
如果設置了該屬性瓦胎,那么規(guī)則將在該屬性指定的值之后在另外一個線程里觸發(fā)。該屬性對應的值為一個長整型尤揣,單位是毫秒
設置DRL文件開始執(zhí)行之后延遲多長時間開始執(zhí)行這條規(guī)則
long型搔啊,無默認值
條件部分(LHS)
- when:規(guī)則條件開始。條件可以單個芹缔,也可以多個坯癣,多個條件一次排列,比如
when
eval(true)
$customer:Customer()
$message:Message(status==0)
上述羅列了三個條件最欠,當前規(guī)則只有在這三個條件都匹配的時候才會執(zhí)行RHS部分示罗。
eval(true):是一個默認的api,true 無條件執(zhí)行芝硬,類似于 while(true)
$message:Message(status==0) 這句話標示的:當前的workingMemory存在Message類型并且status屬性的值為0的Fact對象蚜点,這個對象通常是通過外部java代碼插入或者在已執(zhí)行規(guī)則的RHS部分中insert進去的。
"$message"代表著當前條件的引用的Message實例拌阴。在后續(xù)的條件部分和RHS部分中绍绘,可以使用這個名稱對該FACT對象進行修改或者調(diào)用其方法〕僭撸可選陪拘。
條件可以有組合,比如:
Message(status==0 || (status > 1 && status <=100))如果條件全部是 &&關系纤壁,可以使用“,”來替代左刽,但是兩者不能混用
- Drools提供了十二中類型比較操作符:
> , >= , < , <= , == , !=
contains / not contains / memberOf / not memberOf /matches/ not matches
contains, not contains:顧名思義
memberOf:判斷某個Fact屬性值是否在某個集合中酌媒。
not memberOf:顧名思義
matches:正則表達式匹配欠痴,與java不同的是迄靠,不用考慮'/'的轉義問題
not matches:顧名思義
結果部分(RHS)
當滿足規(guī)則條件,則進入規(guī)則結果部分執(zhí)行喇辽。
RHS可以是純java代碼掌挚,比如:
then
System.out.println("OK"); //會在控制臺打印出ok
end
RHS可以調(diào)用Fact的方法,比如
$message.setXXX(value);
RHS可以使用Drools 原生的方法:
insert:往當前workingMemory中插入一個新的Fact對象
update:更新workingMemory中對應的Fact對象菩咨,會觸發(fā)規(guī)則的再次執(zhí)行
modify:修改吠式,與update語法不同,結果都是更新操作
retract:刪除workingMemory中對應的Fact對象
以上操作可能會觸發(fā)規(guī)則的再次執(zhí)行旦委。具體看規(guī)則中no-loop與lock-on-active屬性的設置奇徒。
RHS也可以調(diào)用規(guī)則文件中定義的方法,方法的定義使用 function 關鍵字
function void console {
System.out.println();
StringUtils.getId();// 調(diào)用外部靜態(tài)方法缨硝,StringUtils必須使用import導入摩钙,getId()必須是靜態(tài)方法
}
關于規(guī)則執(zhí)行
插入Product(discount=1)執(zhí)行以下規(guī)則:
- Demo 1
import com.drools.model.Product;
rule updateDistcount
salience 9
no-loop true
when
productObj:Product(discount > 0);
then
productObj.setDiscount(productObj.getDiscount() + 1);
System.out.println(productObj.getDiscount());
update(productObj);
end
rule otherRule
salience 1
when
productObj : Product(discount > 1);
then
System.out.println("被觸發(fā)了" + productObj.getDiscount());
end
執(zhí)行結果為:
2
被觸發(fā)了2
- Demo 2
去掉no-loop
import com.drools.model.Product;
rule updateDistcount
salience 9
/*no-loop true*/
when
productObj:Product(discount > 0);
then
productObj.setDiscount(productObj.getDiscount() + 1);
System.out.println(productObj.getDiscount());
update(productObj);
end
rule otherRule
salience 1
when
productObj : Product(discount > 1);
then
System.out.println("被觸發(fā)了" + productObj.getDiscount());
end
執(zhí)行結果為:
2
3
4
5
6
7
.....
結果分析:
第一條規(guī)則未設置no-loop屬性,當update后Working Memory中的Fact被修改查辩,重新觸發(fā)規(guī)則胖笛。
第二條規(guī)則未執(zhí)行是由于優(yōu)先級(salience)問題。更詳細的原因后續(xù)補充..
- Demo 3
去掉update操作
import com.drools.model.Product;
rule updateDistcount
salience 9
no-loop true
when
productObj:Product(discount > 0);
then
productObj.setDiscount(productObj.getDiscount() + 1);
System.out.println(productObj.getDiscount());
/*update(productObj);*/
end
rule otherRule
salience 1
when
productObj : Product(discount > 1);
then
System.out.println("被觸發(fā)了" + productObj.getDiscount());
end
執(zhí)行結果為:"2"宜岛。分析如下:
rete算法會將fact匹配到的規(guī)則做記錄列表长踊,然后按照匹配列表中的規(guī)則RHS按照salience的次序執(zhí)行。
所以它是這樣一個過程:接受數(shù)據(jù)輸入萍倡、匹配業(yè)務規(guī)則身弊、做出業(yè)務決策。
當一條規(guī)則導致FACT變更時列敲,可能會導致以上過程重新執(zhí)行(未設置no-loop阱佛、lock-on-active屬性)
- Demo 4
第二條規(guī)則加入lock-on-active true
package org.drools.example.api.dynamic
import org.drools.example.api.model.Product
rule updateDistcount
salience 9
no-loop true
when
productObj:Product(discount > 0);
then
productObj.setDiscount(productObj.getDiscount() + 1);
System.out.println(productObj.getDiscount());
update(productObj);
end
rule otherRule
lock-on-active true
salience 1
when
productObj : Product(discount > 1);
then
System.out.println("被觸發(fā)了" + productObj.getDiscount());
end
執(zhí)行結果:"2"。結果分析如下:
詳見lock-on-active屬性
Drools Kie Api 中幾個核心概念:
- KieServices
通過KieServices.Factory.get()方式獲得戴而,是一個單例的凑术、線程安全的,為其他Kie工具提供服務
KieServices是Kie項目的中心所意,通過其可以獲取的各種對象來完成規(guī)則構建淮逊、管理和執(zhí)行等操作
其中有的方法分為兩大類:getX()和newX(),其中扶踊,get只會返回一個對應單例對象的引用泄鹏,new則會重新創(chuàng)建一個對象
- KieContainer
從KieServices中獲得,其會借助KieProject來初始化秧耗、構造KieModule并將放入KieRepository中
是一個給定的KieModule中所有KieBase的存放容器
- KieRepository
KieRepository是一個單例對象命满,它是一個存放KieModule的倉庫,KieModule由kmodule.xml文件定義.
- KieProject
KieContainer可以通過KieProject來查找KieModule定義的信息绣版,并根據(jù)這些信息構造KieBase和KieSession胶台;
KieProject 為物理概念;通過KieProject中的KieModule文件可定義KieBase杂抽、KieSession。
- ClasspathKieProject
ClasspathKieProject實現(xiàn)了KieProject接口,它提供了根據(jù)類路徑中的META-INF/kmodule.xml文件構造KieModule的能力
- KieBase
KieBase就是一個知識倉庫晌杰,包含了若干的規(guī)則缀遍、流程、方法等杭朱,但是不包含運行時的數(shù)據(jù)
- KieSession
KieSession就是一個跟Drools引擎打交道的會話阅仔,其基于KieBase創(chuàng)建
KieContainer創(chuàng)建KieSession是一種較為方便的做法,其實他本質(zhì)上是從KieBase中創(chuàng)建出來
- 幾個核心概念之間的關系
KieProject即一個規(guī)則工程弧械,為物理概念其對應的邏輯概念為KieModule八酒。
KieProject包含了一個kmodule.xml文件。其中定義了kmodule刃唐、kbase和ksession等屬性羞迷。
kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="SimpleRuleKBase" packages="com.us.person">
<ksession name="simpleRuleKSession"/>
</kbase>
</kmodule>
kmodule里面包含了一個或多個kbase。
每一個kbase都有一個唯一的名字(name)画饥,不能重復衔瓮。
packages 對應的值是命名空間。規(guī)則引擎會根據(jù)這里定義的包來查找規(guī)則定義文件抖甘∪劝埃可以定義多個包,以逗號分隔衔彻。
每一個kbase下面包含一個或多個ksession薇宠。
每一個ksession都有一個唯一的名字(name),不能重復米奸。
下面內(nèi)容為純摘錄部分昼接,待后續(xù)整理
使用規(guī)則工程(最終形態(tài)為k-jar)
首先創(chuàng)建KieServices
然后通過getKieClasspathContainer方法獲得KieContainer
KieContainer根據(jù)KieProject中的kmodule文件(ClasspathKieProject實現(xiàn)的)
創(chuàng)建KieModule并放入KieRepository
然后KieContainer創(chuàng)建KieBase
之后KieBase創(chuàng)建KieSession規(guī)則會話到規(guī)則引擎
通過KieSession即可執(zhí)行規(guī)則或Process。