在 Drools 中尋找匹配規則列表
1. 引言
在許多基於 Drools 的應用程式中,不僅要執行規則,還要了解實際觸發了哪些規則。本文將探討在 Drools 中追蹤已觸發規則的多種方法,包括自訂AgendaEventListener和RuleContext-based實用方法。
2. Maven 依賴項
KIE(Knowledge Is Everything,知識就是一切)是 Drools 的核心框架。它為在應用程式程式碼之外定義和執行業務規則及其他知識資產提供了基礎架構。首先,讓我們將kie-ci依賴項匯入pom.xml中:
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<version>10.0.1</version>
</dependency>
當使用基於 Maven 的 KIE 模組和動態規則載入時,需要kie-ci ,這是現代 Drools 設定中的常見模式。
3. 流口水範例
在本節中,我們將透過一個簡單的範例來示範如何在實作中使用 Drools。
3.1 人類模型
為了示範規則追蹤的工作原理,我們從一個簡單的Person領域模型開始:
public class Person {
private String name;
private int age;
private boolean eligibleToVote;
private boolean priorityVoter;
public Person(String name, int age) {
this.name = name;
this.age = age;
this.eligibleToVote = false;
this.priorityVoter = false;
}
// standard getters and setters
}
此類定義了一些與投票決策相關的屬性,例如個人的年齡和選民身分標誌。規則會在評估條件時更新這些欄位。
3.2 規則文件
接下來,我們在 DRL 檔案中定義規則:
rule "Check Voting Eligibility Event"
when
$person : Person(age >= 18)
then
$person.setEligibleToVote(true);
update($person);
end
rule "Senior Priority Voting Event"
when
$person : Person(age >= 65)
then
$person.setPriorityVoter(true);
update($person);
end
每條規則都會評估Person物件上的特定條件,並更新對應的欄位:
- 檢查投票資格-如果某人符合最低年齡要求,則將其標記為有資格投票。
- 老年人優先投票權-賦予老年人優先投票權。
4. 使用AgendaEventListener找出符合規則列表
在本節中,我們將介紹一個自訂的AgendaEventListener ,它可以自動記錄每個觸發的規則。
4.1. AgendaEventListener
Drools 提供了多個內建事件監聽器,讓我們可以對規則引擎生命週期的不同階段做出反應。為了追蹤會話期間實際觸發了哪些規則,我們可以註冊一個AgendaEventListener並專門監聽afterMatchFired()事件。此回呼會在規則成功執行後立即呼叫。讓我們建立TrackingAgendaEventListener類別:
public class TrackingAgendaEventListener extends DefaultAgendaEventListener {
private final List<Match> matchList = new ArrayList<>();
@Override
public void afterMatchFired(AfterMatchFiredEvent event) {
matchList.add(event.getMatch());
}
public List<String> getFiredRuleNames() {
List<String> names = new ArrayList<>();
for (Match m : matchList) {
names.add(m.getRule().getName());
}
return names;
}
}
這裡,監聽器僅重寫了afterMatchFired()方法,足以記錄規則的執行情況。每次規則觸發時,監聽器都會從事件中提取規則名稱並將其新增至追蹤器。現在,我們需要將監聽器附加到KieSession :
KieSession kieSession = new DroolsBeanFactory().getKieSession();
TrackingAgendaEventListener listener = new TrackingAgendaEventListener();
kieSession.addEventListener(listener);
這種方法與 DRL 檔案完全解耦——無需對規則本身進行任何修改——使其成為一種乾淨且非侵入性的規則執行審計方法。
4.2 測試
讓我們建立一個測試來驗證我們的TrackingAgendaEventListener是否正確記錄了在 Drools 會話執行期間觸發的規則名稱:
@Test
public void givenRuleFired_whenListenerAttached_thenRuleIsTracked() {
// Given
Person person = new Person("Bob", 65);
TrackingAgendaEventListener listener = new TrackingAgendaEventListener();
kieSession.addEventListener(listener);
// When
kieSession.insert(person);
kieSession.fireAllRules();
kieSession.dispose();
// Then
assertFalse(listener.getFiredRuleNames().isEmpty());
assertTrue(listener.getFiredRuleNames().contains("Check Voting Eligibility Event"));
assertTrue(listener.getFiredRuleNames().contains("Senior Priority Voting Event"));
}
首先,我們建立一個Person實例,其屬性滿足規則集中兩個規則的條件。在本例中,一位 65 歲的人士會同時觸發「檢查投票資格事件」規則(因為年齡 ≥ 18 歲)和「老年人優先投票事件」規則(因為年齡 ≥ 65 歲)。接下來,我們將自訂的TrackingAgendaEventListener註冊到KieSession中。透過在插入事實之前附加監聽器,我們可以確保捕獲會話期間觸發的每個規則。插入Person事實並執行規則後,我們檢查監聽器以確認結果。
5. 使用RuleContext尋找符合規則列表
在本節中,我們介紹了一種基於RuleContext方法,該方法允許在 DRL 檔案中進行手動追蹤。
5.1. RuleContext
雖然AgendaEventListener提供了一種從規則庫外部觀察規則執行的非侵入式方法,但在某些情況下,我們可能需要更精細地控制規則的記錄時間和方式。 Drools透過RuleContext API 公開了此功能,該 API 允許從 DRL 檔案本身存取目前正在執行的規則。為了使用這種方法,我們定義了一個包含靜態方法的小型實用程式類,該方法接受規則上下文和我們的RuleTracker :
public class RuleUtils {
public static void track(RuleContext ctx, RuleTracker tracker) {
String ruleName = ctx.getRule().getName();
tracker.add(ruleName);
}
}
此方法從 Drools 上下文中提取活動規則的名稱,並將其新增至追蹤器。接下來,我們建立RuleTracker類別:
public class RuleTracker {
private final List<String> firedRules = new ArrayList<>();
public void add(String ruleName) {
firedRules.add(ruleName);
}
public List<String> getFiredRules() {
return firedRules;
}
}
與基於監聽器的方法不同,這種機制允許規則編寫者精確決定規則追蹤在結果區塊中的特定位置。為了使方法在 .drl 檔案中可用,我們在規則檔案的頂部匯入它,並在規則內部直接呼叫該方法:
package com.baeldung.drools.rules
import com.baeldung.drools.matched_rules.Person;
import com.baeldung.drools.matched_rules.RuleTracker;
import static com.baeldung.drools.matched_rules.RuleUtils.track;
rule "Check Voting Eligibility"
when
$person : Person(age >= 18)
$tracker : RuleTracker()
then
track(drools, $tracker);
$person.setEligibleToVote(true);
update($person);
end
rule "Senior Priority Voting"
when
$person : Person(age >= 65)
$tracker : RuleTracker()
then
track(drools, $tracker);
$person.setPriorityVoter(true);
update($person);
end
5.2 測試
讓我們建立一個單元測試來驗證,當Person實例滿足我們的規則條件時,基於RuleContext的追蹤是否能正確記錄所有觸發的規則:
@Test
public void givenPerson_whenRulesFire_thenContextTracksFiredRules() {
// Given
Person person = new Person("John", 70);
RuleTracker tracker = new RuleTracker();
// When
kieSession.insert(person);
kieSession.insert(tracker);
kieSession.fireAllRules();
kieSession.dispose();
// Then
List<String> fired = tracker.getFiredRules();
assertTrue(fired.contains("Check Voting Eligibility"));
assertTrue(fired.contains("Senior Priority Voting"));
assertTrue(person.isEligibleToVote());
assertTrue(person.isPriorityVoter());
}
我們建立一個 70 歲的Person ,該對象滿足兩條規則(檢查投票資格和老年人優先投票),並建立一個RuleTracker來捕獲已觸發的規則。然後,我們將這兩個事實插入KieSession中,並呼叫fireAllRules(),函數來執行這些規則。最後,我們斷言RuleTracker中存在兩個預期的規則,並且Person實例的eligibleToVote和priorityVoter欄位已正確更新。
6. 結論
本文探討了兩種用於確定 KIE 會話期間觸發了哪些 Drools 規則的實用技術。使用AgendaEventListener可以讓我們透明地追蹤規則執行情況,而無需修改規則文件,因此對於大多數應用程式來說,這是一種簡潔且非侵入式的選擇。另一方面, RuleContext方法透過直接在 DRL 內部記錄規則觸發情況,提供了明確的規則層級控制。與往常一樣,原始碼已發佈在 GitHub 上。