對于做軟件的人來說,唯一不變的就是變化。此為行業真理。而對于復雜業務系統的邏輯組件的定義不得不多考慮一下業務的可擴展性,來應對客戶的變化。選擇Rule Engine是一個不錯的方案。
Drools 是用 Java 語言編寫的開放源碼規則引擎。Drools 允許使用聲明方式表達業務邏輯??梢允褂梅?XML 的本地語言編寫規則(這點很重要,本人之前曾用過自己公司的一套業務規則組件,無論是編寫還是調試都很麻煩),從而便于學習和理解。并且,還可以將 Java 代碼直接嵌入到規則文件中,使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
這里面有個狀態的條件判斷state == CustomerState.UNCENSORED ,then 關鍵字后面的便是符合條件的處理邏輯,只要是java程度都可以看懂,比xml類的rule文件好懂了許多。
接下來
語法說明:
文件頭部分:
package drools.java.demo;定義包名,等同于命名空間
import drools.java.demo.Machine;導入java類
global java.util.List myGlobalList
;
此關鍵字讓規則引擎知道,myGlobalList對象應該可以從規則中訪問
.
function:類似于公用方法的抽象,如下定義后,各個同一文件下的rule都可以使用
function void setTestsDueTime(Machine machine, int numberOfDays) {
setDueTime(machine, Calendar.DATE, numberOfDays);
}
rule:定義了一個規則
rule "<name>"
<attribute>*
when
<conditional element>*
then
<action>*
end
<name> 即rule的名字標識
<attribute>:

常用的屬性:
no-loop :true 條件結果更改后,修改此條件且定義為no-loop:true的規則不會再重新執行。
lock-on-active:true 可以看作是no-loop的加強版,當條件結果更改后,不但修改此條件的規則不會重新執行,文件中的任何規則(其 active-lock 屬性被設為 true
)不會重新執行。
salience
:100 使用它可以讓規則執行引擎知道應該啟動規則的結果語句的順序。具有最高顯著值的規則的結果語句首先執行;具有第二高顯著值的規則的結果語句第二執行,依此類推。當您需要讓規則按預定義順序啟動時,這一點非常重要。
其他屬性的解釋請見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:業務規則的地方,略。
2.用法
規則文件定義好后,就該是怎么使用它了
如上圖,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構造方法演示了如何去讀入一個rule文件,并構建了一個RuleBase對象(RuleBase 是一個包含了rule文件的所有規則的集合)
executeRules方法定義了如何使用規則文件中定義的那些內容,用RuleBase構建一個WorkingMemory對象,再執行fireAllRules()方法。
WorkingMemory 代表了與rulebase鏈接的session會話,也可以看作是工作內存空間。如果你要向內存中插入一個對象可以調用insert()方法,同理,更新一個對象使用update()方法。WorkingMemory還有一個setGlobal()方法,用來設置規則內可以引用的對象(相當于規則的全局變量)。
3.小技巧
可以一次把所有的rule文件都載入內存中存放,這樣就不用每次執行都讀取文件。
如果規則文件被修改,也可以用過一個方法來判斷是否需要重新載入rule文件
比如:根據文件的最后修改時間,與內存中對應對象的時間做比較
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,比我說得好多了。