JAVA隨筆

           

          規則容器的實現

          希望大家提出保貴意見~


          優點:

          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 ) &amp;&amp; $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) &amp;&amp; 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' &amp;&amp; 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 ) &amp;&amp; $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()方法。 -->
              <!-- 其中&amp;&amp;在讀入程序時,將用&&(與)替換, -->
              <!-- 解釋一下,其中要使用輸入參數必須要以"#"號開頭
              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 &amp;&amp; #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>
             
             
             
          很簡單吧~

          posted on 2007-04-28 09:49 曲靜波 閱讀(1339) 評論(0)  編輯  收藏 所屬分類: others

          導航

          統計

          常用鏈接

          留言簿(3)

          隨筆分類(9)

          隨筆檔案(8)

          文章分類

          友情鏈接

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 尼玛县| 贵定县| 鄢陵县| 东阿县| 建水县| 汽车| 平舆县| 平原县| 寿阳县| 台中县| 剑阁县| 南雄市| 鲁山县| 乌拉特中旗| 彭泽县| 东丽区| 鄂州市| 友谊县| 黄石市| 肃北| 宁国市| 碌曲县| 屏边| 连城县| 八宿县| 阿克陶县| 阜南县| 察雅县| 酉阳| 凉城县| 玛纳斯县| 吉林省| 大同市| 临西县| 马公市| 平果县| 泌阳县| 宜兰县| 赤水市| 海阳市| 高邮市|