目錄
- 1. 創建Drools環境(引入Drools相關依賴包、現在都流行spring boot,故最簡單有效的依賴才是最好的,kie-spring內部自行依賴了drools相關核心的依賴包)
- 2. 了解Drools語法及其含義(LHS、RHS、Fact)
- 3. 幾種實作運行Drools規則引擎方法
- 4. Drl規則內容幾種寫法測驗代碼
- 5. 規則引擎引發的舉一反三,自己實作一個規則引擎
Drools規則引擎,網上大把相關的文章介紹,但我感覺不夠直白,理解有些困難,且知識點沒有集中比較分散、有些還是早前版本的內容,對與新手來說上手可能比較慢,而且比較容易走彎路,故我在深入研究并實踐于專案中后,在空閑時間花費精力整理了這篇文章,分享出來,便大家快速上手,
1. 創建Drools環境(引入Drools相關依賴包、現在都流行spring boot,故最簡單有效的依賴才是最好的,kie-spring內部自行依賴了drools相關核心的依賴包)
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.55.0.Final</version>
</dependency>
2. 了解Drools語法及其含義(LHS、RHS、Fact)
-
DRL檔案基本格式:
package rules.testwrod //包名,必需,這是邏輯上,與物理路徑無關 import xxxxx; //可選,匯入要使用的類名(還支持直接匯入靜態方法) global java.util.List myGlobalList;//可選,定義全域變數(該變數由外部setGlobal傳入) function getResult(...){ //可選,自定義函式 } query "query_gt_0"(...) //可選,自定義查詢(僅只有LHS內容) $result:規則Pattern end rule “test001” //規則名稱,必需,且需唯一 when //規則開始關鍵字,必需 //這里如果為空 則表示 eval(true); LHS內容(即:規則條件) then //規則條件結束關鍵字,必需,后面部份則是RHS內容(即:規則觸發的邏輯) System.out.println(“hello drools!”); end //規則結束關鍵字 -
涉及的名詞解釋:
-
LHS:條件部分又被稱之為 Left Hand Side,簡稱為 LHS,在一個規則當中 when 與 then 中
間的部分就是 LHS 部分,在 LHS 當中,可以包含 0~n 個條件,如果 LHS 部分沒空的話,
那么引擎會自動添加一個 eval(true)的條件,由于該條件總是回傳 true,所以 LHS 為空的規
則總是回傳 true,LHS涉及的匹配元素用法如下:-
Pattern 模式,語法:事實型別(約束),其中約束是可選的,如:Person(age>18),意思是:匹配作業記憶體中是Person型別且age>18,若存在則為true,即命中該條規則;Pattern 模式支持多個,之間使用空格或換行即可;【通俗點:當前作業記憶體中有沒有這個型別的物件(fact)】
-
欄位約束,即Pattern 模式中括號中的部份,一般有:單值限制(如:age>18)、復合值限制(Person(sex in (0,1)),注:暫支持in與not in)和多限制(如:age>18 && age<30 或 age ( (> 30 && < 40) || (> 20 && < 25) )) 3種限制模式;欄位約束之間支持:||、&&、and、or、,(逗號即為AND)【通俗點:當前作業內中的這個型別(fact)的物件屬性還需滿足相關的約束條件】
-
條件元素 eval,條件元素 eval 本實上是包羅萬象的,它允許執行任何語意代碼(回傳一個 boolean 原型)【通俗點:動態解釋執行代碼邏輯,與js的eval有類似功能】
-
條件元素 not,用于檢查在作業記憶體中不存在某東西,把"not"看作“一定沒有……”的意思
-
條件元素 exists,用于檢查在作業記憶體中存在某型別(fact),把"exists"看作“至少有一個……”的意思,(如果匹配到多個事實fact物件,也只會觸發執行一次RHS中邏輯)
-
條件元素 forall,用于檢查在作業記憶體中所有的物件(fact)都必需滿足Pattern 模式,若有1個不滿足,則為false,只有全部滿足才為true;(如果匹配到多個事實fact物件,也只會觸發執行一次RHS中邏輯)
-
條件元素 from, 讓用戶指定任意的資源,用于 LHS 模式的資料匹配,這允許引擎在非作業記憶體資料的基礎上進行推斷,資料源可以是一個系結變數的一個子欄位,或者方法呼叫的結果,它是一個超強結構,允許開箱即可與其他應用程式組件或框架集成使用【通俗點:from后面是指定一個自定義的資料源,from前面是from后面結果得到的,類似sql中的select field=value from table;】
-
條件元素 collect,允許規則在來自特定資源或作業記憶體的一個物件集合上進行推斷【通俗點:就是將符合匹配到多個事實fact物件累加到一起形成一個Collection集合】
-
條件元素 accumulate,是一個更靈活強大的 collect 形式,它主要做的事是允許規則迭代整個
一個物件的集合,為每個元素定制執行動作,并在結束時回傳一個結果物件, accumulate
既支持預定義的累積函式的使用,或也可以使用行內的自定義代碼,簡化的語法如下:accumulate( < source pattern 源模式 >; < functions 函式 > [;< constraints >] ),其中函式除了內置的還可以自定義JAVA函式,只需使用import accumulate 型別(該型別需實作AccumulateFunction介面) 自定義方法名;
示例代碼:
accumulate(Message(createBy=="zuowj",$id:id);$countNum:count($id);$countNum>1) //含義:查找作業記憶體中有Message型別的且過濾條件為(createBy=="zuowj")fact事實物件,并取出id,然后對所有的id進行count,最后判斷count的結果是否>1,轉換為SQL理解就是: //select id from Message where createBy='zuowj' group by id having count(id)>1;這樣應該好理解吧!inline 的語法結構:
< result pattern >from accumulate(< source pattern >,init(< init code >),action(< action
code >),reverse(< reverse code >),result(< result expression >) )< source pattern >:這個表示源模式,用法:也就是我們常用手 Object(xx:XX 屬性) 這個會去匹配每一個源物件;
< init code >:用法說明:init 是做初始化用的,簡單的說,在 source pattern 遍歷完之后 就已經觸發,有點像 for 的開頭;
< action code >: 用法說明:action 會執行所以滿足條件的源物件進行操作,像是 for的方法體,在里面可寫 java code;
< reverse code >: 這是一個可選的被選方言的語意代碼塊,如果存在,將為不再匹配資源模式的每個資源物件執行,這個代碼塊的目的是不做在< action code > 塊中做的任何計算,所以,當一個資源物件被修改或洗掉收時,引擎可能做遞減計算,極大地提升了這些操作的性能;
< result expression >: 回傳值,是根據 action 上面兩個遍歷出來的結果進行一個返
回,這個回傳值中也可以進行計算,
< result pattern >: 回傳值型別,在< result expression >回傳值的型別再一次進行匹
配,如果匹配不成功則回傳 false,示例代碼:
$res:String() from accumulate(Message(createBy=="zuowj",$cont:content),init(String allContent="";),action(allContent +=$cont;),result(allContent)) //含義:for回圈遍歷作業記憶體中Message型別且過濾條件為(createBy=="zuowj")的fact物件,初始化設定allContent="",每次執行allContent +=$cont,遍歷完成后將allContent回傳給#res變更接收,類似JAVA for代碼如下: // String res="",allContent=""; //for (Object o:List<Object>){ // if(o instanceof Message && ((Message)o).getContent()=="zuowj"){ // allContent+=((Message)o).getContent(); // } //} //res=allContent;
-
-
RHS:結果部分又被稱之為 Right Hand Side,簡稱為 RHS,在一個規則當中 then 后面部分就是 RHS,只有在 LHS 的所有條件都滿足時 RHS 部分才會執行,RHS 部分是規則真正要做事情的部分,可以將因條件滿足而要觸發的動作寫在該部分當中,在 RHS 當中可以使用 LHS 部分當中定義的系結變數名、設定的全域變數、或者是直接撰寫 Java 代碼(對于要用到的 Java 類,需要在規則檔案當中用 import 將類匯入后方能使用,這點和 Java 檔案的撰寫規則相同,且不建議在RHS中寫條件判斷,如果需要條件判斷,那么請重新考慮將其放在 LHS 當中,否則就違背了使用規則的初衷,),同時在 RHS 里面,還提供了一些對當前 Working Memory 實作快速操作的宏函式或物件,比如 insert/insertLogical、update/modify 和 retract 就可以實作對當前 WorkingMemory 中的 Fact 物件進行新增、修改或者是洗掉;如果您覺得還要使用 Drools 當中提供的其它方法,那么您還可以使用另一外宏物件 drools,通過該物件可以使用更多的操作當前 Working Memory 的方法;同時 Drools 還提供了一個名為 kcontext 的宏物件,使我們可以通過該物件直接訪問當前 Working Memory 的 KnowledgeRuntime,另外,通過注冊Channel實作命中規則后通過channels[通道名].send發送訊息,并傳遞給JAVA代碼的訂閱回呼方法;
-
function:函式類似JAVA中的有回傳值的方法,將RHS中涉及一些重復的動作封裝定義成函式(支持定義入參),能夠有效的簡少重復邏輯的撰寫,但注意,函式的應用是不建議直接寫在LHS塊中的,若需使用需使用eval關鍵字,類似:
eval(hello("夢在旅途")); -
query:查詢是一種搜索作業記憶體中與指定條件匹配的事實的簡單方法,因此,它只包含規則的
LHS 的結構,因此既不指定“when”也不指定“then”,查詢具有可選的引數集,每個引數
可以可選地鍵入,如果未給出型別,則假定型別為 Object,引擎將根據需要嘗試強制值,
查詢名稱對于 KieBase 是全域的;因此不要向同一 RuleBase 的不同包添加相同名稱的查詢,
要回傳結果,請使用 ksession.getQueryResults(“name”),其中“name”是查詢
的名稱,這將回傳查詢結果的串列,這允許您檢索與查詢匹配的物件,查詢以 query 關鍵字開始,以 end 關鍵字結束,在 package 當中一個查詢要有唯一的名稱,查詢的內容就是查詢的條件部分,條件部分內容的寫法與規則的 LHS 部分寫法非常相同, -
global:全域變數(類似java中的final static定義的變數 ),同一個session中所有rule都共享使用(如果多個包使用相同的識別符號宣告了全域變數,那么它們必須有相同的型別,并且它們所有都會參考相同的全域變數的值),全域變數沒有被插入到作業記憶體,因此,全域變數絕不能被用來在規則中建立條件,除非它是一個恒定不變的值,引擎不能知道全域變數的改變,不能跟蹤它們的變化,還需注意:常量值是不能改變的、包裝類是不能改變的、類似 javaBean,List 這類的操作,是可以改變內容的,但記憶體地址不會變;
-
-
Drools的屬性說明(一般在在rule 名稱 與when之前設定屬性):
- Salience 優先級,作用是用來設定規則執行的 優先級, salience 屬性的值是一個數字,數字越大執行優先級越高,同時它的值可以是一個負數,默認情況下,規則的 salience 默認值為 0;
- no-loop 防止死回圈,作用是用來控制已經執行過的規則在條件再次滿足時是否再次執行,no-loop 屬性的值是一個布爾型,默認情況下規則的no-loop 屬性的值為 false,如果 no-loop 屬性值為true,那么就表示該規則只會被引擎檢查一次,如果滿足條件就執行規則的 RHS 部分;
- date- effective 日期比較小于等于,該屬性是用來控制規則只有在到達后才會觸發,在規則運行時,引擎會自動拿當前作業系統的時候與 date-effective 設定的時間值進行比對,只有 當前系統時間>=date-effective 設定的時間值時,規則才會觸發執行,否則執行將不執行;
- date- exspires 日期比較大于,該屬性的作用與 date-effective 屬性恰恰相反,當前系統時間<date-expires 值,date-expires 的作用是用來設定規則的有效期,引擎在執行規則的時候,會檢查規則有沒有 date-expires 屬性,如果有的話,那么會將這個屬性的值與當前系統時間進行比對,如果大于系統時間,那么規則就執行,否則就不執行;
- Dialect 方言,該屬性用來定義規則當中要使用的語言型別,支持 mvel 和 java,默認是java;
- Enabled 是否可用,用來定義一個規則是否可用的,如是設為false則不會執行該規則,默認為true;
- lock- on-active 規則只執行一次,當在規則上使用 ruleflow-group屬性或 agenda-group 屬性的時候,將 lock-on-action屬性的值設定為 true,可能避免因某些 Fact 物件被修改而使已經執行過的規則再次被激活執行;
- activation-group 分組,作用是將若干個規則劃分成一個組,用一個字串來給這個組命名,這樣在執行的時候, 具有相同 activation- - group 屬性的規則中只要有一個會被執行,其它的規則都將不再執行;
- 其它的:agenda- group 議程分組、auto-focus 焦點分組;
- ruleflow-group 規則流,在使用規則流的時候要用到 ruleflow-group 屬性,該屬性的值為一個字串,作用是用來將規則劃分為一個個的組,然后在規則流當中通過使用 ruleflow-group 屬性的值,從而使用對應的規則,該屬性會通過流程的走向確定要執行哪一條規則,在規則流中有具體的說明,
-
drools中相關核心型別說明:
- fact:即將一個普通的 JavaBean 插入到規則的 WorkingMemory 當中后的物件(如:kieSession.insert( javaBean物件)),規則可以對 Fact物件進行任意的讀寫操作,當一個 JavaBean 插入到 workingMemory 當中變成 Fact 之后(回傳一個FactHandler),Fact 物件不是原來的 JavaBean物件的副本,而是原來 JavaBean 物件的參考;
- KieServices:就是一個中心,通過它來獲取的各種物件來完成規則構建、管理和執行等操作;(KieServices.Factory.get() 獲得)
- KieContainer:是一個 KieBase 的容器,利用 KieContainer 來訪問 KBase 和 KSession 等資訊;(KieServices.newKieContainer()獲得)
- KieBase:可以理解為是一個知識倉庫,包含了若干的規則、流程、方法等,在 Drools 中主
要就是規則和方法,KieBase 本身并不包含運行時的資料之類的,如果需要執行規則 KieBase
中的規則的話,就需要根據 KieBase 創建 KieSession;(KieContainer.getKieBase() 或 newKieBase()獲得) - KieSession:就是一個跟 Drools 引擎打交道的會話,基于 KieBase 創建,它會包含運行時資料,包含“事實 Fact”,并對運行時資料事實進行規則運算;分為兩類:有狀態的 KieSession(在多次與規則引擎進行互動中,維護會話的狀態)、無狀態的 StatelessKieSession(隔離了每次與規則引擎的互動,不會維護會話的狀態);(KieBase.newStatelessKieSession() 或 newKieSession()獲得)
- KieRepository:是一個單例物件,它是一個存放 KieModule 的倉庫;
- KieProject:KieContainer 通過 KieProject 來初始化、構造 KieModule,并將 KieModule 存放到 KieRepository 中,然后 KieContainer 可以通過 KieProject 來查找 KieModule 定義的資訊,并根據這些資訊構造 KieBase 和KieSession;
- ClasspathKieProject:ClasspathKieProject 實作了 KieProject 介面,它提供了根據類路徑中的 META-INF/kmodule.xml 檔案構造 KieModule 的能力,也就是我們能夠基于 Maven 構造 Drools 組件的基本保障之一;
3. 幾種實作運行Drools規則引擎方法
-
直接使用KieHelper動態的將規則drl字串添加到規則引擎中并運行:
String drl = "package zuowenjun.drools.rule.demo\n" + "import cn.zuowenjun.model.Message;\n" + "import java.util.List;\n" + "rule \"test rule 1\"\n" + "when \n" + "$res:String() from accumulate(Message(createBy==\"zuowj\",$cont:content),init(String allContent=\"\";),action(allContent +=$cont;),result(allContent))"+ "then\n" + "System.out.println($res +\"---rule 2\");\n" + "end"; KieBase kieBase = new KieHelper().addContent(drl, ResourceType.DRL).build(); StatelessKieSession kieSession = kieBase.newStatelessKieSession(); kieSession.execute(list); -
直接使用KieHelper動態的將drl檔案添加到規則引擎中并運行:
//rule.drl檔案(放在resources自定義rules目錄中,注:路徑可自定義) package zuowenjun.drools.rule.demo import cn.zuowenjun.model.Message; rule "test rule2" when $msg:Message(createBy=="zuowj") then System.out.println("hello zuowj! --rule2"); $msg.setReplyBy("rule2"); end注:如下使用的是ResourceFactory.newClassPathResource獲取drl檔案,其實里面封裝了很多的獲取資源的方式(如:newFileResource、newByteArrayResource、newInputStreamResource等)
//JAVA代碼: Resource resource = ResourceFactory.newClassPathResource("rules/rule.drl"); KieHelper helper = new KieHelper(); KieBase kieBase = helper.addResource(resource, ResourceType.DRL).build(); StatelessKieSession kieSession = kieBase.newStatelessKieSession(); kieSession.execute(msg); -
直接通過drools spring組態檔實作規則添加及運行:
<!--在resources目錄中添加drools spring的組態檔(如:spring-drools.xml) --> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:kie="http://drools.org/schema/kie-spring" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://drools.org/schema/kie-spring http://drools.org/schema/kie-spring.xsd"> <kie:kmodule id="kmodule"> <kie:kbase name="kbase" packages="zuowenjun.drools.rules"> </kie:kbase> </kie:kmodule> <bean id="kiePostProcessor" /> </beans>JAVA代碼:
//配置,此處只需通過@ImportResource匯入組態檔,自動注冊成BEAN即可,當然這里是一個單獨組態檔,實際也可以直接放在spring boot 的applcation的啟動類上即可, @Configuration @ImportResource("classpath:spring-drools.xml") public class DroolsBeansConfig { } //BEAN類中直接使用即可 @Component public class RuleDemo { @Autowired private KieBase kbase;//KieBase是單例 public Object checkRule(Message msg){ StatelessKieSession kieSession = kbase.newStatelessKieSession();//session這里盡可能每次都重新創建,成本也比較低,不要搞成單例的,這里是無狀態的,用有狀態的也行 kieSession.execute(msg); return msg; } } //如下是上面所有實體中用到的Message類(普通的javaBean) public class Message { private Long id; private String title; private String createBy; private Date createDate; private String content; private Long enabledFlag; private Boolean isReply; private String replyBy; private Date replyDate; private String replyContent; //省略getter、setter方法 ... } -
還有一種是通過動態創建Kjar來實規則添加及運行,關鍵步驟如下:
創建 pom.xml-》創建 kmodule.xml-》添加規則內容-》后面是創建session-》執行即可;
代碼就不再貼出了,可詳見網上資源,
4. Drl規則內容幾種寫法測驗代碼
public Object checkRule(Object msg) {
List<String> drlContentList=new ArrayList<>();
//當一個Fact物件為集合物件時的判斷
//這個是當把某個集合(List)當成一個fact傳入作業記憶體中后,規則有效
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"import java.util.List;\n" +
"rule \"test rule 0\"\n" +
"when \n" +
"$list:List(size>0) \n" +
"$msg:Message(createBy==\"zuowj\") from $list \n " +
"then\n" +
"System.out.println(\"hello zuowj! ---rule 0\");\n" +
"$msg.setReplyBy(\"rule 0\");\n" +
"end");
//普通Pattern 模式+欄位約束
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 1\"\n" +
"when \n" +
"$msg:Message(createBy==\"zuowj\")\n " +
"then\n" +
"System.out.println(\"hello zuowj! ---rule 1\");\n" +
"$msg.setReplyBy(\"rule 1\");\n" +
"end");
//accumulate 行內方式(類似for回圈處理)
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 2\"\n" +
"when \n" +
"exists(Message(createBy==\"zuowj\"))\n"+
"$res:String() from accumulate(Message(createBy==\"zuowj\",$cont:content),init(String allContent=\"\";),action(allContent +=$cont;),result(allContent))"+
"then\n" +
"System.out.println($res +\"---rule 2\");\n" +
"end");
//accumulate 普通函式方式
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 2-2\"\n" +
"when \n" + "accumulate(Message(createBy==\"zuowj\",$id:id);$countNum:count($id);$countNum>1) \n"+
"then\n" +
"System.out.println(\"count number:\"+ $countNum +\"---rule 2-2\");\n" +
"end");
//not,不滿足時
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 3\"\n" +
"when not Message()\n" +
"then\n" +
"System.out.println(\"no message don't say hello! ---rule 3\");\n" +
"end");
//exists,匹配執行一次
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 4\"\n" +
"when exists(Message(createBy==\"zuowj\"))\n" +
"then\n" +
"System.out.println(\"exists Message(createBy==zuowj) fact! ---rule 4\");\n" +
"end");
//forall,作業記憶體中所有fact物件必需都滿足時才匹配規則
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 5\"\n" +
"when forall(Message(createBy==\"zuowj\"))\n" +
"then\n" +
"System.out.println(\"for all Message(createBy==zuowj) fact! ---rule 5\");\n" +
"end");
//collect,將作業記憶體中所有fact物件添加到同一個集合中
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.Message;\n" +
"rule \"test rule 6\"\n" +
"when Message() && $msgs:List(size>=9) from collect(Message(createBy==\"zuowj\"))\n" +
"then\n" +
"System.out.println(\"collect all Message fact(size=\" + $msgs.size() +\")! ---rule 6\");\n" +
"end");
KieHelper kieHelper=new KieHelper();
for(String drl:drlContentList){
kieHelper.addContent(drl,ResourceType.DRL);
}
KieBase kieBase = kieHelper.build();
StatelessKieSession kieSession = kieBase.newStatelessKieSession();
if (msg instanceof List){
kieSession.execute((List<?>)msg);
} else{
kieSession.execute(msg);
}
return msg;
}
//單元測驗
/**
* @author zuowenjun
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {FmsHelperApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class RuleTests {
@Autowired
private RuleDemo ruleDemo;
@Test
public void testRule() {
List<Message> msgList=new ArrayList<>();
for(int i=1;i<=10;i++) {
int n=i;
Message msg = new Message() {
{
setCreateBy("zuowj");
setContent("hello drools" + String.valueOf(n));
}
};
if (n==1){
msg.setCreateBy("zuowenjun.cn");
}
msgList.add(msg);
}
Object obj = ruleDemo.checkRule(msgList);
System.out.println(JsonUtils.deserializer(obj));
}
}
5. 規則引擎引發的舉一反三,自己實作一個規則引擎
思路:1.定義規則內容(即:規則執行單元),2.定義貫穿整個規則執行鏈條的背景關系,內部就放fact、global等,具體實作參照如下示例代碼【注意:如果僅是示例測驗代碼,并不規范,僅為演示提供思路】,整個規則執行采取:責任鏈的設計模式,即:每個規則只負責滿足自己條件的執行邏輯,最后更新背景關系中相關的內容,
//規則鏈背景關系,里面就包含fact集合,全域物件及執行過的rule
public class RuleChainContext {
public List<Object> factList;
public static Map<String, Object> global;
public RuleUnit execedRule;
}
//規則執行單元抽象類(這里用抽象類而沒有用介面,是因為我要限定組織邏輯,可以理解為模板用法)
public abstract class RuleUnit {
public RuleUnit nextExecedRule;
protected String name;
public abstract String getName();
public abstract boolean matchWhen(RuleChainContext context);
public abstract void doThen(RuleChainContext context);
public final void execute(RuleChainContext context) {
if (matchWhen(context)) {
doThen(context);
}
if (context.execedRule == null) {
context.execedRule = this;
}
context.execedRule.nextExecedRule = this;
}
}
通過單元測驗模擬呼叫:
@Test
public void testDefRules() {
List<RuleUnit> ruleUnitList = new ArrayList<>();
ruleUnitList.add(new RuleUnit() {
@Override
public String getName() {
name= "rule-1";
return name;
}
@Override
public boolean matchWhen(RuleChainContext context) {
return context.factList.stream().anyMatch(f->f instanceof Integer && 1==(Integer)f);
}
@Override
public void doThen(RuleChainContext context) {
System.out.println("rule[include 1] do");
//TODO:context
}
});
ruleUnitList.add(new RuleUnit() {
@Override
public String getName() {
name= "rule-2";
return name;
}
@Override
public boolean matchWhen(RuleChainContext context) {
return context.factList.stream().anyMatch(f->f instanceof Integer && 2==(Integer)f);
}
@Override
public void doThen(RuleChainContext context) {
System.out.println("rule[include 2] do");
//TODO:context
}
});
RuleChainContext context=new RuleChainContext();
context.factList=new ArrayList<>();
context.factList.add(1);//加入1則觸發規則1
context.factList.add(2);//加入2則觸發規則2,若減少規則相應減少
for(RuleUnit ruleUnit:ruleUnitList){
ruleUnit.execute(context);
}
System.out.println("result context:\n" + JsonUtils.deserializer(context));
}
最終結果:
rule[include 1] do
rule[include 2] do
result context:
{"factList":[1,2],"execedRule":{"nextExecedRule":{"nextExecedRule":null,"name":"rule-2"},"name":"rule-1"}}
從輸出的結果可以看出,還是可以達到規則引擎的簡單效果的,當然如果想在生產環境實際應用自己實作的“類規則引擎”代碼,實作規則與執行分開,也可將規則執行單元(RuleUnit)實作類單獨放到一個JAR包,然后再借助于URLClassLoader實作動態加載并添加自定義的實作規則執行單元(RuleUnit)的類,最后執行即可,【.NET方面的同學實作亦同理】
注:文中相關名詞解釋來源于網上,并非原創,我這里僅為知識點總結!
可參考相關drools系列文章:
Drools_miemieY89-CSDN博客
邵飛翔的圖書館 (360doc.com)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288682.html
標籤:其他
上一篇:Dubbo 的設計思想,真優秀!
