希望大家提出保貴意見~
優點:
1、規則規范業務流程,且現有規則已經實現不僅使用在流程平臺上且可以其他模塊,如:作業計劃。僅需配置。
2、支持xml中配置表達式,工程人員將輸入/輸出參數使用表達式配置進行賦值(不需書寫任何代碼),故而影響流程的流向。
3、支持xml中配置規則分組,并將規則賦予優先級,規則會按照規則分組優先級執行規則,直到滿足規則條件退出。
4、支持除表達式復雜的業務邏輯,工程人員可以開發java代碼實現業務方法,在xml中簡單配置即可以實現復雜業務邏輯。
5、支持listener,在調用規則之前、之后都會觸發before(),after()方法。工程人員可按業務編寫多個lisener,使lisener有效只需要簡單xml配置。
6、支持輸入輸出參數的驗證,根據二次開發人員的xml配置,按輸入輸出參數配置類型進行驗證,若輸入/輸出參數不符合業務要求則拋出異常。
發展:
1、xml的web頁面配置,提供eclipse plugin配置,將需要手動xml配置的地方使用web,eclipse plugin的方式實現工具配置,
其中可實現,若需要判斷值,如執行類型,工程人員在配置xml時每次要查數據庫對應找出值配上,而現在只要求二次二發人員提供取數據庫表的方法,
規則配置工具會將表中的內存如:1,草稿;2,轉派;這些內容以下拉列表方式供工程人員選對,以節省工程人員的麻煩。
2、支持drools。
3、與使用規則的組件更有機的結果,如流程平臺、作業計劃。
依賴關系:
1、文件配置
2、castor
3、ongl
使用:
規則關心的是輸入/輸出參數,也即將輸入參數以java.util.Map傳入,通過key="參數名稱",value="參數類型"
根據傳入的參數組合條件,將輸出參數以java.util.Map類型返回,也通過key="參數名稱",value="參數類型"
以com.boco.eoms.commons.rule.test.service.RuleServiceFacadeTest.java 為例測試自定義java規則及規則分組調用
為說明例子,我編寫了com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample.java及同包下的Rule1InputParameter2Sample.java
做為輸入參數
com.boco.eoms.commons.rule.Rule1OutputParameter1Sample.java及同包下Rule1OutputParameter2Sample.java做為輸出參數
com.boco.eoms.commons.rule.Rule1Sample.java及com.boco.eoms.commons.rule.Rule2Sample.java為例說明自定義java規則及規則分組
com.boco.eoms.commons.rule.sample.Rule1ListenerSample.java為自定義listener
com.boco.eoms.commons.rule.sample.RuleSample.xml配置規則,先介紹輪廓,之后會詳細介紹
rule的說明是個比較不好說明的東西,所以有些枯燥,:-)
配置文件如下說明:
<?xml version="1.0" encoding="UTF-8"?>
<ruleEngine>
<groups>
<!--規則分組(路由)-->
<group id="group1">
<!-- 引用Rule2Sample,Rule1Sample兩個規則,優先級為2,優先級別以-1...正無窮遞增,即-1為最低優先級,默認規則優先級為-1 -->
<groupRef ruleId="Rule2Sample" pri="2" />
<groupRef ruleId="Rule1Sample" pri="1" />
</group>
</groups>
<!-- 表達式樣式 ,使用自定義java規則不需定義,先不用觀注-->
<expStyles>
<expStyle id="rule1Express"
style=" ( $parameter != $l{springbean.getNames} || false ) && $text ? $r{springbean.getName} : 3 " />
</expStyles>
<rules>
<!-- 規則 className為規則包+類名 ,id為唯一標識,記好了,將來調用時要用RuleId-->
<rule className="com.boco.eoms.commons.rule.sample.Rule1Sample"
id="Rule1Sample">
<!-- 輸入參數定義 ,由使用規則組件的開發人員定義,rule1InputParameter1Sample為key,-->
<!--類型為com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample,這里要定義正確呀,否則驗證不通過,也不通路由的~-->
<input>
<parameter name="rule1InputParameter1Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample" />
<parameter name="rule1InputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter2Sample" />
</input>
<!-- 輸出參數,expression為表達式,暫不關注,name及type和輸入參數一樣,drl是支持drools的預留接口,expStyleId表達式樣式ID,暫不關心-->
<output>
<parameter
expression=""
name="rule1OutputParameter1Sample"
type="com.boco.eoms.commons.rule.sample.Rule1OutputParameter1Sample"
drl=""
expStyleId="rule1Express" />
<parameter
expression=""
name="rule1OutputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1OutputParameter2Sample"
drl=""
expStyleId="rule1Express" />
</output>
<!--自定義listener,Rule1ListenerSample為自定義,RuleCheckListener為系統提供驗證listener驗證,若不需要驗證將RuleCheckListener去掉即可(不推薦)-->
<listeners>
<listener
name="com.boco.eoms.commons.rule.sample.Rule1ListenerSample" />
<listener
name="com.boco.eoms.commons.rule.listener.RuleCheckListener" />
</listeners>
</rule>
<rule className="com.boco.eoms.commons.rule.sample.Rule2Sample"
id="Rule2Sample">
<input>
<parameter name="rule1InputParameter1Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample" />
<parameter name="rule1InputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter2Sample" />
</input>
<output>
<parameter
expression=" ( ${rule1InputParameter1Sample.name} != 'value3' || false) && true ? $b{springbean.getName} : 3 "
name="rule1OutputParameter1Sample"
type="com.boco.eoms.commons.rule.sample.Rule1OutputParameter1Sample"
drl="classpath:com.boco.eoms.commons.rule.sample.rule1Parameter1Sample.drl"
expStyleId="rule1Express" />
<parameter
expression=" rule1InputParameter1Sample.name != '234' && rule1InputParameter1Sample.age >10 ? rule1InputParameter1Sample.name : rule1InputParameter1Sample.age "
name="rule1OutputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1OutputParameter2Sample"
drl="classpath:com.boco.eoms.commons.rule.sample.rule1Parameter2Sample.drl"
expStyleId="rule1Express" />
</output>
<listeners>
<listener
name="com.boco.eoms.commons.rule.sample.Rule1ListenerSample" />
<listener
name="com.boco.eoms.commons.rule.listener.RuleCheckListener" />
</listeners>
</rule>
</rules>
</ruleEngine>
OK,現在我們看看自定義java寫了什么
public class Rule1Sample extends RuleService {
/**
* @param ruleListeners
* @param rules
* @param rule
* @param xmlPath
*/
public Rule1Sample(List ruleListeners, RuleEngine rules, Rule rule,
String xmlPath) {
super(ruleListeners, rules, rule, xmlPath);
}
/**
* 二次開發人員需實現的業務接口
*
* @param map
* 根據配置文件中input的配置,二次開發人員取出配置的參數,
* 如sample配置key="rule1InputParameter1Sample",value="com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample"
* key="rule1Parameter2InputSample",value="com.boco.eoms.commons.rule.sample.Rule1InputParameter2Sample"
* 這時二次發人員在execute中按這種方式取參數
* com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample
* param1=(com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample)
* map.get("rule1InputParameter1Sample");
* com.boco.eoms.commons.rule.sample.Rule1InputParameter2Sample
* param2=(com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample)
* map.get("rule1InputParameter2Sample");
*
* @return 根據配置文件output的配置,二次開發人員需返回配置的參數 如sample配置
* key="rule1OutputParameter1Sample",value="com.boco.eoms.commons.rule.sample.Rule1OutputParameter1Sample"
* key="rule1OutputParameter2Sample",value="com.boco.eoms.commons.rule.sample.Rule1OutputParameter2Sample"
* 配置人員需按業務要求,返回
* map.put("rule1OutputParameter1Sample",com.boco.eoms.commons.rule.sample.Rule1OutputParameter1Sample);
* map.put("rule1OutputParameter2Sample",com.boco.eoms.commons.rule.sample.Rule1OutputParameter2Sample);
* return map;
* @throws RuleException
*/
public Map execute(Map map,Rule rule) throws RuleException {
// 規則業務
// 取輸入參數
Rule1InputParameter1Sample inputParam1 = (Rule1InputParameter1Sample) map
.get("rule1InputParameter1Sample");
Rule1InputParameter2Sample inputParam2 = (Rule1InputParameter2Sample) map
.get("rule1InputParameter2Sample");
Map outMap = new HashMap();
Rule1OutputParameter1Sample outParam1 = new Rule1OutputParameter1Sample();
Rule1OutputParameter2Sample outParam2 = new Rule1OutputParameter2Sample();
// 模擬規則,年齡大于10數,姓名不為匿名,則outParam1設為可通過
if (inputParam1.getAge() > 10
&& !"anonym".equals(inputParam1.getName())) {
// 為說明業務邏輯所以將setName放入判斷條件
outParam1.setName(inputParam1.getName());
outParam1.setOk(true);
} else {
// 為說明業務邏輯所以將setName放入判斷條件
outParam1.setName(inputParam1.getName());
outParam1.setOk(false);
}
// 性別為男則通過
if ("male".equals(inputParam2.getSex())) {
outParam2.setSex("male");
outParam2.setOk(true);
} else {
outParam2.setSex("male");
outParam2.setOk(false);
}
outMap.put("rule1OutputParameter1Sample", outParam1);
outMap.put("rule1OutputParameter2Sample", outParam2);
// 返回輸出參數
return outMap;
}
}
以Rule1Sample.java為例,需繼承RuleService并實現execute方法,業務邏輯寫在該方法內,如Rule1Sample.java
按照配置的輸入/輸出參數進行操作。
同時開發人員需要 像RuleServiceFacadeTest這樣調用,按注釋調用,請看注釋
public class RuleServiceFacadeTest extends TestCase {
private RuleServiceFacade facade;
private Map<String, Object> inputMap;
private Rule1InputParameter1Sample inputParam1;
private Rule1InputParameter2Sample inputParam2;
private String xmlPath = "";
private String groupId = "";
/**
* 輸入參數初始化
*/
protected void setUp() throws Exception {
facade = RuleServiceFacade.create();
// 測試輸入參數類弄與xml是否匹配
inputMap = new HashMap<String, Object>();
inputParam1 = new Rule1InputParameter1Sample();
inputParam2 = new Rule1InputParameter2Sample();
inputParam1.setAge(10);
inputParam1.setName("qjb");
inputParam2.setSex("male");
inputMap.put("rule1InputParameter1Sample", inputParam1);
inputMap.put("rule1InputParameter2Sample", inputParam2);
xmlPath = "classpath:com/boco/eoms/commons/rule/sample/RuleSample.xml";
groupId = "group1";
super.setUp();
}
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* 以classpath:com/boco/eoms/commons/rule/sample/RuleSample.xml為配置文件,調用rule1Sample為ruleId,以inputMap為輸入參數,
*
*/
public void testInvokeRuleService() {
try {
facade.invokeRuleService(xmlPath, "Rule1Sample", inputMap);
} catch (RuleException e) {
fail();
}
}
/**
* 以規則分組(路由)方式調用,調用groupId即group1,按照xml配置是調用了兩個rule,按照優先級(數字由大到小)先后調用
* 以不同輸入參數調用
*/
public void testInvokeRuleGroupForDiffInputMap() {
Map<String, Map> map = new HashMap<String, Map>();
Map outMap = null;
// 以不同輸入參數調用
map.put("Rule1Sample", inputMap);
map.put("Rule2Sample", inputMap);
try {
outMap = facade.invokeRuleGroupForDiffInputMap(xmlPath, groupId,
map);
} catch (RuleException e) {
fail();
}
checkOutMap(outMap);
}
/**
* 以規則分組(路由)方式調用,調用groupId即group1,按照xml配置是調用了兩個rule,按照優先級(數字由大到小)先后調用
* 以相同輸入參數調用
*
*/
public void testInvokeRuleGroupForSampeInputMap() {
Map outMap = null;
try {
outMap = facade.invokeRuleGroupForSampeInputMap(xmlPath, groupId,
inputMap);
} catch (RuleException e) {
e.printStackTrace();
fail();
}
checkOutMap(outMap);
}
/**
* 驗證輸出參數,用于測試
*
* @param outMap
*/
private void checkOutMap(Map outMap) {
Rule1OutputParameter2Sample outPram2 = (Rule1OutputParameter2Sample) outMap
.get("rule1OutputParameter2Sample");
assertEquals(outPram2.getSex(), "male");
assertEquals(outPram2.isOk(), true);
}
}
這時再看自定義的listener
com.boco.eoms.commons.rule.sample.Rule1ListenerSample.java
public class Rule1ListenerSample implements IRuleListener {
private Logger logger = Logger.getLogger(this.getClass());
/**
* 執行規則后調用
*/
public void after(Map inputMap, Rule rule) throws RuleException {
logger.debug(rule.getId() + " after");
}
/**
* 執行規則前調用
*/
public void before(Map outputMap, Rule rule) throws RuleException {
logger.debug(rule.getId() + " before");
}
}
需要實現IRuleListener.java的after及before方法
ok,運行com.boco.eoms.commons.rule.RuleServiceFacadeTest.java測試用例。
暈了吧~,重新看下,OK,以上內容為自定義調用,下面說說表達式方式調用
以com.boco.eoms.commons.rule.test.service.ExpressionRuleServiceTest.java 為例測試表達式的規則
按com.boco.eoms.commons.rule.sample.ExpressionRuleSample.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<ruleEngine>
<!-- 表達式樣式,用于規則工具定義,暫不關心 -->
<expStyles>
<expStyle id="rule1Express"
style=" ( $parameter != $l{springbean.getNames} || false ) && $text ? $r{springbean.getName} : 3 " />
</expStyles>
<rules>
<!-- 這里一定要配className="com.boco.eoms.commons.rule.service.ExpressionRuleService"這個類,表達式類-->
<rule
className="com.boco.eoms.commons.rule.service.ExpressionRuleService"
id="ExpressionRule">
<!-- 輸入參數定義 ,由使用規則組件的開發人員定義,rule1InputParameter1Sample為key,-->
<!--類型為com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample,這里要定義正確呀,否則驗證不通過,也不通路由的~-->
<input>
<parameter name="rule1InputParameter1Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample" />
<parameter name="rule1InputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter2Sample" />
</input>
<!-- 輸出參數,expression為表達式,暫不關注,name及type和輸入參數一樣,drl是支持drools的預留接口,expStyleId表達式樣式ID,暫不關心-->
<output>
<!--expression 表達式 rule1InputParameter1Sample.age指com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample中的getAge()方法。 -->
<!-- 其中&&在讀入程序時,將用&&(與)替換, -->
<!-- 解釋一下,其中要使用輸入參數必須要以"#"號開頭
rule1InputParameter1Sample 及rule2InputParameter1Sample從map中取出(這里開發人員不必關心,由規則幫你處理)
if(rule1InputParameter1Sample.getAge()>5 && "qjb".equals(rule1InputParameter1Sample.getName()){
return rule1InputParameter1Sample.getResult();
}
else{
return rule1InputParameter1Sample.getStr(rule1InputParameter1Sample.getName());
}
規則容器將返回值寫入輸出map中,以name=rule1OutputParameter1Sample為key值,value即為返回值
-->
<parameter
expression="#rule1InputParameter1Sample.age>5 && #rule1InputParameter1Sample.name=='qjb'?#rule1InputParameter1Sample.result:#rule1InputParameter1Sample.getStr(#rule1InputParameter1Sample.name)"
name="rule1OutputParameter1Sample"
type="java.lang.String"
drl="" expStyleId="" />
<!-- 再來解釋下第二個輸出參數表達式
if(rule1InputParameter1Sample.getAge()>5)){
rule1InputParameter1Sample.setAge(5);
}
else{
rule1InputParameter1Sample.setAge(6);
}
return rule1InputParameter1Sample;
-->
<parameter
expression="#rule1InputParameter1Sample.age>5?#rule1InputParameter1Sample.setAge(5):#rule1InputParameter1Sample.setAge(6),#rule1InputParameter1Sample"
name="rule1OutputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample"
drl="" expStyleId="" />
</output>
<!--listener第一個例子,不需要表達式配置功能一樣-->
<listeners>
<listener
name="com.boco.eoms.commons.rule.sample.Rule1ListenerSample" />
<listener
name="com.boco.eoms.commons.rule.listener.RuleCheckListener" />
</listeners>
</rule>
</rules>
</ruleEngine>
運行com.boco.eoms.commons.rule.test.service.ExpressionRuleServiceTest.java測試用例
仔細看下測試用例,應該很好理解,
恭喜,恭喜,熟練掌握rule了吧?:-)
痛苦的再寫下去,再說說rule的擴展吧~
基于rule去擴展是很簡單的~
舉個例子,其實表達式的支持,就是我擴展的一個類,看下
com.boco.eoms.commons.rule.service.ExpressionRuleService.java
public class ExpressionRuleService extends RuleService {
@Override
protected Map<String, Object> execute(Map<String, Object> inputMap,
Rule rule) throws RuleException {
// 創建表達式解析service
OgnlExpressionService oes = OgnlExpressionService.create(null);
Map<String, Object> outMap = new HashMap<String, Object>();
// 取輸出參數
for (Iterator it = rule.getOutput().getParameters().iterator(); it
.hasNext();) {
Parameter para = (Parameter) it.next();
// 將輸出參數配置的表達式結果按照配置名稱寫入outMap
outMap.put(para.getName(), oes.getValue(para.getExpression(),
inputMap));
}
return outMap;
}
/**
* @param ruleListeners
* @param rules
* @param rule
* @param xmlPath
*/
public ExpressionRuleService(List ruleListeners, RuleEngine rules,
Rule rule, String xmlPath) {
super(ruleListeners, rules, rule, xmlPath);
}
}
看下,沒什么內容吧,只需繼承RuleService,重寫execute方法,在里面實現你的業務邏輯,
其實就是自定義規則,但也可以寫成通用的呀。就像將來要擴展的drools一樣,將來新增個
DroolsRuleService就OK了。這時在xml配置時,在className配置剛剛寫的DroolsRuleService的類就OK了。
這樣就實現了對drools的支持
<rule
className="com.boco.eoms.commons.rule.service.ExpressionRuleService"
id="ExpressionRule">
<!-- 輸入參數定義 ,由使用規則組件的開發人員定義,rule1InputParameter1Sample為key,-->
<!--類型為com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample,這里要定義正確呀,否則驗證不通過,也不通路由的~-->
<input>
<parameter name="rule1InputParameter1Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter1Sample" />
<parameter name="rule1InputParameter2Sample"
type="com.boco.eoms.commons.rule.sample.Rule1InputParameter2Sample" />
</input>
再說說監聽,有人說你的監聽不是沒用的嗎,我要實現自定義業務邏輯,只需在execute方法最前端及后端加上我要在listener中要做的事,
這不一樣能解決嗎,OK,我們來看下,完全沒錯,可以解決,除非你想將listener的東西藕荷在業務邏輯,當然listener的before(),after()方法
不包含你的業務邏輯。再說下,若你不實現自定義規則呢,你怎么實現在before(),after()方法的內容呢,哈哈。再以一個例子說明,其實rule的
參數驗證機制就是一個listener實現的,看一下com.boco.eoms.rule.listener.RuleCheckListener.java
public class RuleCheckListener implements IRuleListener {
public void after(Map map, Rule rule) throws RuleException {
// 取輸入參數
for (Iterator outputIt = rule.getOutput().getParameters().iterator(); outputIt
.hasNext();) {
Parameter para = (Parameter) outputIt.next();
// 驗證map中的所存的對象與xml配置是否相符
RuleConfigWrapper.checkMapType(map, para);
}
}
public void before(Map map, Rule rule) throws RuleException {
// 取輸入參數
for (Iterator inputIt = rule.getInput().getParameters().iterator(); inputIt
.hasNext();) {
Parameter para = (Parameter) inputIt.next();
// 驗證map中的所存的對象與xml配置是否相符
RuleConfigWrapper.checkMapType(map, para);
}
}
}
這就是輸入/輸出參數與xml配置的驗證呀,呵呵~
OK,現在編寫你的listenr吧
只需實現IRuleListener,并實現after(),befor()方法,
配置時在xml中配置,貼一段
<listeners>
<listener
name="com.boco.eoms.commons.rule.sample.Rule1ListenerSample" />
<listener
name="com.boco.eoms.commons.rule.listener.RuleCheckListener" />
</listeners>
很簡單吧~