對于做軟件的人來說,唯一不變的就是變化。此為行業(yè)真理。而對于復(fù)雜業(yè)務(wù)系統(tǒng)的邏輯組件的定義不得不多考慮一下業(yè)務(wù)的可擴(kuò)展性,來應(yīng)對客戶的變化。選擇Rule Engine是一個不錯的方案。
Drools 是用 Java 語言編寫的開放源碼規(guī)則引擎。Drools 允許使用聲明方式表達(dá)業(yè)務(wù)邏輯。可以使用非 XML 的本地語言編寫規(guī)則(這點很重要,本人之前曾用過自己公司的一套業(yè)務(wù)規(guī)則組件,無論是編寫還是調(diào)試都很麻煩),從而便于學(xué)習(xí)和理解。并且,還可以將 Java 代碼直接嵌入到規(guī)則文件中,使Drools 更加吸引人。簡單的概括,就是簡單使用,易于理解。而且它是免費的。
1.rule文件:
rule "rule name"
no-loop
when
customer : Customer( state == CustomerState.UNCENSORED )
then
customer.setState(CustomerState.AUDITING);
CustomerTask task=new CustomerTask();
Post law=userService.getPostByPostCode(Constants.PostCode.ROOT_LAW);
task.setAuditorPost(law);
task.setEntityState(CustomerState.AUDITING);
task.setEntityId(customer.getId());
task.setEntityCode(String.valueOf(customer.getId()));
task.setEntityType(Customer.class.getSimpleName());
task.setTitle(customer.getName()+" test");
taskService.assignmentTask(task);
logger.info("CustomerTask Submit auditorTitle:" + task.getAuditorTitle());
end
這里面有個狀態(tài)的條件判斷state == CustomerState.UNCENSORED ,then 關(guān)鍵字后面的便是符合條件的處理邏輯,只要是java程度都可以看懂,比xml類的rule文件好懂了許多。
接下來
語法說明:
文件頭部分:
package drools.java.demo;定義包名,等同于命名空間
import drools.java.demo.Machine;導(dǎo)入java類
global java.util.List myGlobalList
;
此關(guān)鍵字讓規(guī)則引擎知道,myGlobalList對象應(yīng)該可以從規(guī)則中訪問
.
function:類似于公用方法的抽象,如下定義后,各個同一文件下的rule都可以使用
function void setTestsDueTime(Machine machine, int numberOfDays) {
setDueTime(machine, Calendar.DATE, numberOfDays);
}
rule:定義了一個規(guī)則
rule "<name>"
<attribute>*
when
<conditional element>*
then
<action>*
end
<name> 即rule的名字標(biāo)識
<attribute>:

常用的屬性:
no-loop :true 條件結(jié)果更改后,修改此條件且定義為no-loop:true的規(guī)則不會再重新執(zhí)行。
lock-on-active:true 可以看作是no-loop的加強(qiáng)版,當(dāng)條件結(jié)果更改后,不但修改此條件的規(guī)則不會重新執(zhí)行,文件中的任何規(guī)則(其 active-lock 屬性被設(shè)為 true
)不會重新執(zhí)行。
salience
:100 使用它可以讓規(guī)則執(zhí)行引擎知道應(yīng)該啟動規(guī)則的結(jié)果語句的順序。具有最高顯著值的規(guī)則的結(jié)果語句首先執(zhí)行;具有第二高顯著值的規(guī)則的結(jié)果語句第二執(zhí)行,依此類推。當(dāng)您需要讓規(guī)則按預(yù)定義順序啟動時,這一點非常重要。
其他屬性的解釋請見http://downloads.jboss.com/drools/docs/5.1.1.34858.FINAL/drools-expert/html_single/index.html#d0e2607
when:填寫條件的地方,比如:
Cheese( type == "stilton", price < 10, age == "mature" )或
Cheese( type == "stilton" && price < 10, age == "mature" )
then:業(yè)務(wù)規(guī)則的地方,略。
2.用法
規(guī)則文件定義好后,就該是怎么使用它了
如上圖,file rule定義好后,就該是如何使用它了。最重要的兩個類RuleBase和WorkingMemory
下面是一個example:
public class RulesEngine {
private RuleBase rules;
private boolean debug = false;
public RulesEngine(String rulesFile) throws RulesEngineException {
super();
try {
// Read in the rules source file
Reader source = new InputStreamReader(RulesEngine.class
.getResourceAsStream("../../rules/" + rulesFile));
// Use package builder to build up a rule package
PackageBuilder builder = new PackageBuilder();
// This will parse and compile in one step
builder.addPackageFromDrl(source);
// Get the compiled package
Package pkg = builder.getPackage();
// Add the package to a rulebase (deploy the rule package).
rules = RuleBaseFactory.newRuleBase();
rules.addPackage(pkg);
} catch (Exception e) {
throw new RulesEngineException(
"Could not load/compile rules file: " + rulesFile, e);
}
}
public RulesEngine(String rulesFile, boolean debug)
throws RulesEngineException {
this(rulesFile);
this.debug = debug;
}
public void executeRules(WorkingEnvironmentCallback callback) {
WorkingMemory workingMemory = rules.newStatefulSession();
if (debug) {
workingMemory
.addEventListener(new DebugWorkingMemoryEventListener());
}
callback.initEnvironment(workingMemory);
workingMemory.fireAllRules();
}
}
RulesEngine構(gòu)造方法演示了如何去讀入一個rule文件,并構(gòu)建了一個RuleBase對象(RuleBase 是一個包含了rule文件的所有規(guī)則的集合)
executeRules方法定義了如何使用規(guī)則文件中定義的那些內(nèi)容,用RuleBase構(gòu)建一個WorkingMemory對象,再執(zhí)行fireAllRules()方法。
WorkingMemory 代表了與rulebase鏈接的session會話,也可以看作是工作內(nèi)存空間。如果你要向內(nèi)存中插入一個對象可以調(diào)用insert()方法,同理,更新一個對象使用update()方法。WorkingMemory還有一個setGlobal()方法,用來設(shè)置規(guī)則內(nèi)可以引用的對象(相當(dāng)于規(guī)則的全局變量)。
3.小技巧
可以一次把所有的rule文件都載入內(nèi)存中存放,這樣就不用每次執(zhí)行都讀取文件。
如果規(guī)則文件被修改,也可以用過一個方法來判斷是否需要重新載入rule文件
比如:根據(jù)文件的最后修改時間,與內(nèi)存中對應(yīng)對象的時間做比較
public boolean hasChange(List<RuleFile> ruleFileList){
for(RuleFile ruleFile:ruleFileList){
if(!ruleFile.getLastModifyTime().equals(ruleFileMap.get(ruleFile.getFileName()).getLastModifyTime())){
return true;
}
}
return false;
}
注:具體的helloWorld 請見http://www.ibm.com/developerworks/cn/java/j-drools/#listing12,比我說得好多了。