一. 簡介
Drools是一個基于java的規(guī)則引擎,開源的桶蛔,可以將復(fù)雜多變的規(guī)則從硬編碼中解放出來,以規(guī)則腳本的形式存放在文件中漫谷,使得規(guī)則的變更不需要修正代碼重啟機器就可以立即在線上環(huán)境生效仔雷。
二. 環(huán)境搭建
1. 從drools官網(wǎng)下載最新drools
http://www.drools.org/download/download.html
2. eclipse的drools插件離線安裝
- 從官網(wǎng)下載對應(yīng)版本的插件壓縮包
將下載到的壓縮包解壓,將features,plugins文件夾,拷貝到eclipse的dropins/drools目錄(drools目錄需要自己建立),然后重啟eclipse
3. 使用maven創(chuàng)建drools項目
- 項目的pom中引入drools的依賴
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-test</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>knowledge-api</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>6.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-jsr94</artifactId>
<version>6.4.0.Final</version>
</dependency>
三. 快速入門
- 使用骨架庫創(chuàng)建一個簡單的java maven項目
- 創(chuàng)建規(guī)則文件
Sample.drl
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="rules" packages="rules">
<ksession name="ksession-rules"/>
</kbase>
<kbase name="dtables" packages="dtables">
<ksession name="ksession-dtables"/>
</kbase>
<kbase name="process" packages="process">
<ksession name="ksession-process"/>
</kbase>
</kmodule>
- 定義規(guī)則之間的關(guān)系
kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="rules" packages="rules">
<ksession name="ksession-rules"/>
</kbase>
</kmodule>
- 編寫測試類
package com.sample;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
public class DroolsTest {
public static final void main(String[] args) {
try {
// load up the knowledge base
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("ksession-rules");
// go !
Message message = new Message();
message.setMessage("Hello World");
message.setStatus(Message.HELLO);
kSession.insert(message);
kSession.fireAllRules();
} catch (Throwable t) {
t.printStackTrace();
}
}
public static class Message {
public static final int HELLO = 0;
public static final int GOODBYE = 1;
private String message;
private int status;
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
public int getStatus() {
return this.status;
}
public void setStatus(int status) {
this.status = status;
}
}
}
四. 項目結(jié)構(gòu)說明
五. drools工作流程
六. 規(guī)則文件(.drl文件)
規(guī)則語法:
package /*包名,必須,且必須在第一行,不必與規(guī)則為文件的物理路徑一致舔示,若自定義函數(shù)或查詢屬于同一包名碟婆,不管物理位置如何,都可以調(diào)用*/
import /*需要導(dǎo)入的類名*/
global /*全局變量*/
function /*函數(shù)*/
query /*查詢*/
rule /*規(guī)則,可以有多個*/
規(guī)則文件
package com.qc.drools;
import com.qc.model.Student;
global java.util.List.studentList;
function void sysName(){
System.out.println("我是張三");
}
rule "規(guī)則名稱"
<屬性><值>
when
條件 Left Hand Side
then
結(jié)果 Right Hand Side
package:對一個規(guī)則文件而言,package是必須定義的斩郎,必須放在規(guī)則文件第一行脑融。特別的是,package的名字是隨意的缩宜,不必必須對應(yīng)物理路徑肘迎,跟java的package的概念不同甥温,這里只是邏輯上的一種區(qū)分
import:導(dǎo)入規(guī)則文件需要使用到的外部變量,這里的使用方法跟java相同妓布,但是不同于java的是姻蚓,這里的import導(dǎo)入的不僅僅可以是一個類,也可以是這個類中的某一個可訪問的靜態(tài)方法
比如:
import com.drools.demo.point.PointDomain;
import com.drools.demo.point.PointDomain.getById;
global: 定義global全局變量匣沼,通常用于返回數(shù)據(jù)和提供服務(wù)全局變量與fact不一樣狰挡,引擎不能知道全局變量的改變必須要在插入fact之前,設(shè)置global變量
function: 規(guī)則中的代碼塊释涛,封裝業(yè)務(wù)操作加叁,提高代碼復(fù)用函數(shù)以function開頭,其它與JAVA方法類似業(yè)務(wù)代碼書寫采用的是標(biāo)準(zhǔn)JAVA語法
rule:定義一個規(guī)則唇撬。rule "ruleName"
一個規(guī)則可以包含三個部分
屬性部分: 定義當(dāng)前規(guī)則執(zhí)行的一些屬性等它匕,比如是否可被重復(fù)執(zhí)行、過期時間窖认、生效時間等
條件部分: 即LHS豫柬,定義當(dāng)前規(guī)則的條件,如 when Message(); 判斷當(dāng)前workingMemory中是否存在Message對象
結(jié)果部分: 即RHS扑浸,這里可以寫普通java代碼烧给,即當(dāng)前規(guī)則條件滿足后執(zhí)行的操作,可以直接調(diào)用Fact對象的方法來操作應(yīng)用
規(guī)則事例:
rule "name"
no-loop true
when
$message:Message(status == 0)
then
System.out.println("fit");
$message.setStatus(1);
update($message);
end
屬性: activation-group喝噪、agenda-group础嫡、auto-focus、date-effective仙逻、date-expires驰吓、dialect、duration系奉、duration-value檬贰、enabled、lock-on-active缺亮、no-loop翁涤、ruleflow-group、salience
salience: 它的作用是用來設(shè)置規(guī)則執(zhí)行的優(yōu)先級萌踱,salience 屬性的值是一個數(shù)字葵礼,數(shù)字越大執(zhí)行優(yōu)先級越高,同時它的值可以是一個負(fù)數(shù)并鸵。默認(rèn)情況下鸳粉,規(guī)則的salience 默認(rèn)值為0,所以如果我們不手動設(shè)置規(guī)則的salience 屬性园担,那么它的執(zhí)行順序是隨機的
int型届谈,默認(rèn)值為0
no-loop: 在DRL的then子句中枯夜,如果出現(xiàn)insert、update艰山、modify湖雹、retract等方法對實例(Fact)做出修改時,當(dāng)前規(guī)則執(zhí)行完成后會觸發(fā)該規(guī)則使其再執(zhí)行一次曙搬;將no-loop設(shè)置為true則會強制規(guī)則在出現(xiàn)上述方法的情況下也只執(zhí)行一次boolean型摔吏,默認(rèn)值為false
date-effective: 該屬性是用來控制規(guī)則只有在到達(dá)后才會觸發(fā),在規(guī)則運行時纵装,引擎會自動拿當(dāng)前操作系統(tǒng)的時候與date-effective 設(shè)置的時間值進(jìn)行比對征讲,只有當(dāng)系統(tǒng)時間>=date-effective 設(shè)置的時間值時,規(guī)則才會觸發(fā)執(zhí)行搂擦,否則執(zhí)行將不執(zhí)行稳诚。在沒有設(shè)置該屬性的情況下,規(guī)則隨時可以觸發(fā)瀑踢,沒有這種限制。 date-effective 的值為一個日期型的字符串才避,默認(rèn)情況下橱夭,date-effective 可接受的日期格式為“dd-MMM-yyyy”,例如2009 年9 月25 日在設(shè)置為date-effective 的值時桑逝,如果您的操作系統(tǒng)為英文的棘劣,那么應(yīng)該寫成“25-Sep-2009”;如果是英文操作系統(tǒng)“25-九月-2009”
當(dāng)前規(guī)則的生效時間string型楞遏,無默認(rèn)值茬暇;值中需包含日期和時間
date-expires: 該屬性的作用與date-effective 屬性恰恰相反, date-expires 的作用是用來設(shè)置規(guī)則的有效期寡喝,引擎在執(zhí)行規(guī)則的時候糙俗,會檢查規(guī)則有沒有date-expires 屬性,如果有的話预鬓,那么會將這個屬性的值與當(dāng)前系統(tǒng)時間進(jìn)行比對巧骚,如果大于系統(tǒng)時間,那么規(guī)則就執(zhí)行格二,否則就不執(zhí)行劈彪。該屬性的值同樣也是一個日期類型,默認(rèn)格式也是“dd-MMM-yyyy”顶猜,具體用法與date-effective 屬性相同
當(dāng)前規(guī)則的失效時間string型沧奴,無默認(rèn)值;值中需包含日期和時間
enabled: 它是用來定義一個規(guī)則是否可用的长窄。該屬性的值是一個布爾值滔吠,默認(rèn)該屬性的值為true纲菌,表示規(guī)則是可用的,如果手工為一個規(guī)則添加一個enabled 屬性屠凶,并且設(shè)置其enabled 屬性值為false驰后,那么引擎就不會執(zhí)行該規(guī)則
dialect: 該屬性用來定義規(guī)則當(dāng)中要使用的語言類型,目前Drools5 版本當(dāng)中支持兩種類型的語言:mvel 和java矗愧,默認(rèn)情況下灶芝,如果沒有手工設(shè)置規(guī)則的dialect,那么使用的java 語言
設(shè)置規(guī)則所使用的語言
string型唉韭,默認(rèn)值根據(jù)package值判斷夜涕,值域為java或mvel
duration: 對于一個規(guī)則來說,如果設(shè)置了該屬性属愤,那么規(guī)則將在該屬性指定的值之后在另外一個線程里觸發(fā)女器。該屬性對應(yīng)的值為一個長整型,單位是毫秒住诸,代碼清單2-37 里的規(guī)則rule1 添加了duration 屬性驾胆,它的值為3000,表示該規(guī)則將在3000 毫秒之后在另外一個線程里觸發(fā)
設(shè)置DRL文件開始執(zhí)行之后延遲多長時間開始執(zhí)行這條規(guī)則
long型贱呐,無默認(rèn)值
lock-on-active: 當(dāng)在規(guī)則上使用ruleflow-group 屬性或agenda-group 屬性的時候丧诺,將lock-on-action 屬性的值設(shè)置為true,可能避免因某些Fact 對象被修改而使已經(jīng)執(zhí)行過的規(guī)則再次被激活執(zhí)行奄薇〔笛郑可以看出該屬性與no-loop 屬性有相似之處,no-loop 屬性是為了避免Fact 修改或調(diào)用了insert馁蒂、retract呵晚、update 之類而導(dǎo)致規(guī)則再次激活執(zhí)行,這里的lock-on-action 屬性也是起這個作用沫屡,lock-on-active 是no-loop 的增強版屬性饵隙,它主要作用在使用ruleflow-group 屬性或agenda-group 屬性的時候。lock-on-active 屬性默認(rèn)值為false
boolean型谁鳍,默認(rèn)值為false
activation-group: 該屬性的作用是將若干個規(guī)則劃分成一個組癞季,用一個字符串來給這個組命名,這樣在執(zhí)行的時候倘潜,具有相同activation-group 屬性的規(guī)則中只要有一個會被執(zhí)行绷柒,其它的規(guī)則都將不再執(zhí)行。也就是說涮因,在一組具有相同activation-group 屬性的規(guī)則當(dāng)中废睦,只有一個規(guī)則會被執(zhí)行,其它規(guī)則都將不會被執(zhí)行养泡。當(dāng)然對于具有相同activation-group 屬性的規(guī)則當(dāng)中究竟哪一個會先執(zhí)行嗜湃,則可以用類似salience 之類屬性來實現(xiàn)
不基于任何條件將規(guī)則分組string型奈应,無默認(rèn)值
agenda-group: 規(guī)則的調(diào)用與執(zhí)行是通過StatelessSession 或StatefulSession 來實現(xiàn)的,一般的順序是創(chuàng)建一個StatelessSession 或StatefulSession购披,將各種經(jīng)過編譯的規(guī)則的package 添加到session當(dāng)中杖挣,接下來將規(guī)則當(dāng)中可能用到的Global 對象和Fact 對象插入到Session 當(dāng)中,最后調(diào)用fireAllRules 方法來觸發(fā)刚陡、執(zhí)行規(guī)則惩妇。在沒有調(diào)用最后一步fireAllRules 方法之前,所有的規(guī)則及插入的Fact 對象都存放在一個名叫Agenda 表的對象當(dāng)中筐乳,這個Agenda 表中每一個規(guī)則及與其匹配相關(guān)業(yè)務(wù)數(shù)據(jù)叫做Activation歌殃,在調(diào)用fireAllRules 方法后,這些Activation 會依次執(zhí)行蝙云,這些位于Agenda 表中的Activation 的執(zhí)行順序在沒有設(shè)置相關(guān)用來控制順序的屬性時(比如salience 屬性)氓皱,它的執(zhí)行順序是隨機的,不確定的勃刨。 Agenda Group 是用來在Agenda 的基礎(chǔ)之上波材,對現(xiàn)在的規(guī)則進(jìn)行再次分組,具體的分組方法可以采用為規(guī)則添加agenda-group 屬性來實現(xiàn)身隐。agenda-group 屬性的值也是一個字符串各聘,通過這個字符串,可以將規(guī)則分為若干個Agenda Group抡医,默認(rèn)情況下,引擎在調(diào)用這些設(shè)置了agenda-group 屬性的規(guī)則的時候需要顯示的指定某個Agenda Group 得到Focus(焦點)早敬,這樣位于該Agenda Group 當(dāng)中的規(guī)則才會觸發(fā)執(zhí)行忌傻,否則將不執(zhí)行
基于Agenda將規(guī)則分組;只有當(dāng)某個Agenda組獲取到焦點(focus)時搞监,該組的規(guī)則才會被執(zhí)行string型水孩,默認(rèn)值為MAIN
auto-focus: 前面我們也提到auto-focus 屬性,它的作用是用來在已設(shè)置了agenda-group 的規(guī)則上設(shè)置該規(guī)則是否可以自動獨取Focus琐驴,如果該屬性設(shè)置為true俘种,那么在引擎執(zhí)行時,就不需要顯示的為某個Agenda Group 設(shè)置Focus绝淡,否則需要宙刘。對于規(guī)則的執(zhí)行的控制,還可以使用Agenda Filter 來實現(xiàn)牢酵。在Drools 當(dāng)中悬包,提供了一個名為org.drools.runtime.rule.AgendaFilter 的Agenda Filter 接口,用戶可以實現(xiàn)該接口馍乙,通過規(guī)則當(dāng)中的某些屬性來控制規(guī)則要不要執(zhí)行布近。org.drools.runtime.rule.AgendaFilter 接口只有一個方法需要實現(xiàn)垫释,方法體如下: public boolean accept(Activation activation); 在該方法當(dāng)中提供了一個Activation 參數(shù),通過該參數(shù)我們可以得到當(dāng)前正在執(zhí)行的規(guī)則對象或其它一些屬性撑瞧,該方法要返回一個布爾值棵譬,該布爾值就決定了要不要執(zhí)行當(dāng)前這個規(guī)則,返回true 就執(zhí)行規(guī)則预伺,否則就不執(zhí)行
與agenda-group配合使用订咸,設(shè)置焦點的是否可以自動獲取boolean型,默認(rèn)值為false
ruleflow-group: 基于ruleflow將規(guī)則分組string型扭屁,無默認(rèn)值算谈,作用是用來將規(guī)則劃分為一個個的組,然后在規(guī)則流當(dāng)中通過使用ruleflow-group 屬性的值料滥,從而使用對應(yīng)的規(guī)則
Left Hand Side: Conditions / LHS —匹配模式(Patterns)
沒有字段約束的Pattern
Person()
有文本字段約束的Pattern
Person( name == “bob” )
字段綁定的Pattern
Person( $name : name == “bob” )
變量名稱可以是任何合法的java變量然眼,$是可選的,可用于區(qū)分字段和變量
Fact綁定的Pattern
$bob : Person( name == “bob” )
字段綁定的Pattern
變量約束的Pattern
Person( name == $name )
比較操作符: > >= < <= == != contains / not contains / memberOf / not memberOf /matches/ not matches
memberOf: 判斷某個Fact屬性值是否在某個集合中葵腹,與contains不同的是他被比較的對象是一個集合高每,而contains被比較的對象是單個值或者對象
not memberOf: 與memberOf的作用相反
matches: 正則表達(dá)式匹配,與java不同的是践宴,不用考慮'/'的轉(zhuǎn)義問題
not matches: 正好相反
Right Hand Side:
insert: 往當(dāng)前workingMemory中插入一個新的Fact對象鲸匿,會觸發(fā)規(guī)則的再次執(zhí)行,除非使用no-loop限定
update: 更新workingMemory中的Fact對象
modify: 修改阻肩,與update語法不同带欢,結(jié)果都是更新操作
retract: 刪除