第2-4-4章 規則引擎Drools規則屬性-業務規則管理系統-元件化-中臺

假裝文藝範兒 發表於 2022-11-25

5. 規則屬性

前面我們已經知道了規則體的構成如下:

rule "ruleName"
    attributes
    when
        LHS
    then
        RHS
end

本章節就是針對規則體的attributes屬性部分進行講解。Drools中提供的屬性如下表(部分屬性):

屬性名 說明
salience 指定規則執行優先順序
dialect 指定規則使用的語言型別,取值為java和mvel
enabled 指定規則是否啟用
date-effective 指定規則生效時間
date-expires 指定規則失效時間
activation-group 啟用分組,具有相同分組名稱的規則只能有一個規則觸發
agenda-group 議程分組,只有獲取焦點的組中的規則才有可能觸發
timer 定時器,指定規則觸發的時間
auto-focus 自動獲取焦點,一般結合agenda-group一起使用
no-loop 防止死迴圈

5.1 enabled屬性

enabled屬性對應的取值為true和false,預設值為true。

用於指定當前規則是否啟用,如果設定的值為false則當前規則無論是否匹配成功都不會觸發。

rule "rule_comparison_notMemberOf"
    //指定當前規則不可用,當前規則無論是否匹配成功都不會執行
    enabled false
    when
        ComparisonOperatorEntity(names not memberOf list)
    then
        System.out.println("規則rule_comparison_notMemberOf觸發");
end

5.2 dialect屬性

dialect屬性用於指定當前規則使用的語言型別,取值為java和mvel,預設值為java。

注:mvel是一種基於java語法的表示式語言。

mvel像正規表示式一樣,有直接支援集合、陣列和字串匹配的運算子。

mvel還提供了用來配置和構造字串的模板語言。

mvel表示式內容包括屬性表示式,布林表示式,方法呼叫,變數賦值,函式定義等。

5.3 salience屬性

salience屬性用於指定規則的執行優先順序,取值型別為Integer。數值越大越優先執行。每個規則都有一個預設的執行順序,如果不設定salience屬性,規則體的執行順序為由上到下。

可以透過建立規則檔案salience.drl來測試salience屬性,內容如下:

package test.salience

rule "rule_1"
    when
        eval(true)
    then
        System.out.println("規則rule_1觸發");
end
    
rule "rule_2"
    when
        eval(true)
    then
        System.out.println("規則rule_2觸發");
end

rule "rule_3"
    when
        eval(true)
    then
        System.out.println("規則rule_3觸發");
end

透過控制檯可以看到,由於以上三個規則沒有設定salience屬性,所以執行的順序是按照規則檔案中規則的順序由上到下執行的。接下來我們修改一下檔案內容:

package testsalience

rule "rule_1"
    salience 9
    when
        eval(true)
    then
        System.out.println("規則rule_1觸發");
end

rule "rule_2"
    salience 10
    when
        eval(true)
    then
        System.out.println("規則rule_2觸發");
end

rule "rule_3"
    salience 8
    when
        eval(true)
    then
        System.out.println("規則rule_3觸發");
end

透過控制檯可以看到,規則檔案執行的順序是按照我們設定的salience值由大到小順序執行的。

建議在編寫規則時使用salience屬性明確指定執行優先順序。

5.4 no-loop屬性

no-loop屬性用於防止死迴圈,當規則透過update之類的函式修改了Fact物件時,可能使當前規則再次被啟用從而導致死迴圈。取值型別為Boolean,預設值為false。測試步驟如下:

第一步:編寫規則檔案/resource/rules/noloop.drl

package testnoloop
import com.itheima.drools.entity.Student
/*
    此規則檔案用於測試no-loop屬性
*/
rule "rule_noloop"
    when
    	// no-loop true
        $student:Student(age == 25)
    then
        update($student);//注意此處執行update會導致當前規則重新被啟用
        System.out.println("規則rule_noloop觸發");
end

第二步:編寫單元測試

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

Student student = new Student();
student.setAge(25);

//將資料提供給規則引擎,規則引擎會根據提供的資料進行規則匹配,如果規則匹配成功則執行規則
kieSession.insert(student);

kieSession.fireAllRules();
kieSession.dispose();

透過控制檯可以看到,由於我們沒有設定no-loop屬性的值,所以發生了死迴圈。接下來設定no-loop的值為true再次測試則不會發生死迴圈。

5.5 activation-group屬性

activation-group屬性是指啟用分組,取值為String型別。具有相同分組名稱的規則只能有一個規則被觸發。

第一步:編寫規則檔案/resources/rules/activationgroup.drl

package testactivationgroup
/*
    此規則檔案用於測試activation-group屬性
*/
    
rule "rule_activationgroup_1"
    activation-group "mygroup"
    when
    then
        System.out.println("規則rule_activationgroup_1觸發");
end

rule "rule_activationgroup_2"
    activation-group "mygroup"
    when
    then
        System.out.println("規則rule_activationgroup_2觸發");
end

第二步:編寫單元測試

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

透過控制檯可以發現,上面的兩個規則因為屬於同一個分組,所以只有一個觸發了。同一個分組中的多個規則如果都能夠匹配成功,具體哪一個最終能夠被觸發可以透過salience屬性確定。

5.6 agenda-group屬性

agenda-group屬性為議程分組,屬於另一種可控的規則執行方式。使用者可以透過設定agenda-group來控制規則的執行,只有獲取焦點的組中的規則才會被觸發。

第一步:建立規則檔案/resources/rules/agendagroup.drl

package testagendagroup
/*
	此規則檔案用於測試agenda-group屬性
*/
rule "rule_agendagroup_1"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("規則rule_agendagroup_1觸發");
end

rule "rule_agendagroup_2"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("規則rule_agendagroup_2觸發");
end
//========================================================
rule "rule_agendagroup_3"
    agenda-group "myagendagroup_2"
    when
    then
        System.out.println("規則rule_agendagroup_3觸發");
end

rule "rule_agendagroup_4"
    agenda-group "myagendagroup_2"
    when
    then
        System.out.println("規則rule_agendagroup_4觸發");
end

第二步:編寫單元測試

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();

//設定焦點,對應agenda-group分組中的規則才可能被觸發
kieSession.getAgenda().getAgendaGroup("myagendagroup_1").setFocus();

kieSession.fireAllRules();
kieSession.dispose();

透過控制檯可以看到,只有獲取焦點的分組中的規則才會觸發。與activation-group不同的是,activation-group定義的分組中只能夠有一個規則可以被觸發,而agenda-group分組中的多個規則都可以被觸發。

5.7 auto-focus屬性

auto-focus屬性為自動獲取焦點,取值型別為Boolean,預設值為false。一般結合agenda-group屬性使用,當一個議程分組未獲取焦點時,可以設定auto-focus屬性來控制。

第一步:修改/resources/rules/agendagroup.drl檔案內容如下

package testagendagroup

rule "rule_agendagroup_1"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("規則rule_agendagroup_1觸發");
end

rule "rule_agendagroup_2"
    agenda-group "myagendagroup_1"
    when
    then
        System.out.println("規則rule_agendagroup_2觸發");
end
//========================================================
rule "rule_agendagroup_3"
    agenda-group "myagendagroup_2"
    auto-focus true //自動獲取焦點
    when
    then
        System.out.println("規則rule_agendagroup_3觸發");
end

rule "rule_agendagroup_4"
    agenda-group "myagendagroup_2"
    auto-focus true //自動獲取焦點
    when
    then
        System.out.println("規則rule_agendagroup_4觸發");
end

第二步:編寫單元測試

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

透過控制檯可以看到,設定auto-focus屬性為true的規則都觸發了。

5.8 timer屬性

timer屬性可以透過定時器的方式指定規則執行的時間,使用方式有兩種:

方式一:timer (int: <initial delay> <repeat interval>?)

此種方式遵循java.util.Timer物件的使用方式,第一個參數列示幾秒後執行,第二個參數列示每隔幾秒執行一次,第二個引數為可選。

方式二:timer(cron: <cron expression>)

此種方式使用標準的unix cron表示式的使用方式來定義規則執行的時間。

第一步:建立規則檔案/resources/rules/timer.drl

package testtimer
import java.text.SimpleDateFormat
import java.util.Date
/*
    此規則檔案用於測試timer屬性
*/

rule "rule_timer_1"
    timer (5s 2s) //含義:5秒後觸發,然後每隔2秒觸發一次
    when
    then
        System.out.println("規則rule_timer_1觸發,觸發時間為:" + 
                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end

rule "rule_timer_2"
    timer (cron:0/1 * * * * ?) //含義:每隔1秒觸發一次
    when
    then
        System.out.println("規則rule_timer_2觸發,觸發時間為:" + 
                         new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
end

第二步:編寫單元測試

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
final KieSession kieSession = kieClasspathContainer.newKieSession();

new Thread(new Runnable() {
    public void run() {
        //啟動規則引擎進行規則匹配,直到呼叫halt方法才結束規則引擎
        kieSession.fireUntilHalt();
    }
}).start();

Thread.sleep(10000);
//結束規則引擎
kieSession.halt();
kieSession.dispose();

注意:單元測試的程式碼和以前的有所不同,因為我們規則檔案中使用到了timer進行定時執行,需要程式能夠持續一段時間才能夠看到定時器觸發的效果。

5.9 date-effective屬性

date-effective屬性用於指定規則的生效時間,即只有當前系統時間大於等於設定的時間或者日期規則才有可能觸發。預設日期格式為:dd-MMM-yyyy。使用者也可以自定義日期格式。

第一步:編寫規則檔案/resources/rules/dateeffective.drl

package testdateeffective
/*
    此規則檔案用於測試date-effective屬性
*/
rule "rule_dateeffective_1"
    date-effective "2022-11-13 11:36:49"
    when
    then
        System.out.println("規則rule_dateeffective_1觸發");
end

第二步:編寫單元測試

//設定日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

注意:上面的程式碼需要設定日期格式,否則我們在規則檔案中寫的日期格式和預設的日期格式不匹配程式會報錯。

5.10 date-expires屬性

date-expires屬性用於指定規則的失效時間,即只有當前系統時間小於設定的時間或者日期規則才有可能觸發。預設日期格式為:dd-MMM-yyyy。使用者也可以自定義日期格式。

第一步:編寫規則檔案/resource/rules/dateexpires.drl

package testdateexpires
/*
    此規則檔案用於測試date-expires屬性
*/

rule "rule_dateexpires_1"
    date-expires "2023-11-13 11:41:40"
    when
    then
        System.out.println("規則rule_dateexpires_1觸發");
end

第二步:編寫單元測試

//設定日期格式
System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

注意:上面的程式碼需要設定日期格式,否則我們在規則檔案中寫的日期格式和預設的日期格式不匹配程式會報錯。