80.7. DRL 规则集中的规则单元


规则单元是数据源、全局变量和 DRL 规则组,适用于特定目的。您可以使用规则单元将规则集分区为较小的单元,将不同的数据源绑定到这些单元,然后执行单个单元。规则单元是规则组 DRL 属性的增强替代方案,如规则电缆组或执行控制的激活组。

当您要协调规则执行时,规则单元很有用,以便一个规则单元的完整执行会触发另一个规则单元的开始等。例如,假设您有一组用于数据增强的规则、处理该数据的一组规则,以及从处理的数据中提取输出的另一组规则。如果将这些规则集添加到三个不同的规则单元中,您可以协调这些规则单元,以便完成第一单元的执行会触发第二个单元的开始,第二个单元的完整执行会触发第三个单元的开始。

要定义规则单元,实施 RuleUnit 接口,如下例所示:

规则单元类示例

package org.mypackage.myunit;

public static class AdultUnit implements RuleUnit {
    private int adultAge;
    private DataSource<Person> persons;

    public AdultUnit( ) { }

    public AdultUnit( DataSource<Person> persons, int age ) {
        this.persons = persons;
        this.age = age;
    }

    // A data source of `Persons` in this rule unit:
    public DataSource<Person> getPersons() {
        return persons;
    }

    // A global variable in this rule unit:
    public int getAdultAge() {
        return adultAge;
    }

    // Life-cycle methods:
    @Override
    public void onStart() {
        System.out.println("AdultUnit started.");
    }

    @Override
    public void onEnd() {
        System.out.println("AdultUnit ended.");
    }
}
Copy to Clipboard Toggle word wrap

在本例中,人员 是类型为 Person 的事实来源。规则单元数据源是由给定规则单元处理的数据来源,代表决策引擎用于评估规则单元的入口点。adultAge 全局变量可从属于此规则单元的所有规则访问。最后两种方法是规则单元生命周期的一部分,由决策引擎调用。

决策引擎支持以下可选生命周期方法用于规则单元:

Expand
表 80.1. 规则单元生命周期方法
方法调用的时间

onStart()

规则单元执行开始

onEnd()

规则单元执行结束

onSuspend()

规则单元执行暂停(仅用于 runUntilHalt ()

onResume()

规则单元执行会被恢复(仅用于 runUntilHalt ()

onYield (RuleUnit other)

规则单元中规则的结果会触发执行不同的规则单元

您可以在规则单元中添加一个或多个规则。默认情况下,DRL 文件中的所有规则都与遵循 DRL 文件名命名约定的规则单元自动关联。如果 DRL 文件位于同一软件包中,且名称与实现 RuleUnit 接口的类相同,则 DRL 文件中的所有规则都会隐式属于该规则单元。例如,org.mypackage.myunit 软件包中的 AdultUnit.drl 文件中的所有规则都自动是规则单元 org.mypackage.myunit.AdultUnit 的一部分。

要覆盖此命名惯例并明确声明 DRL 文件中的规则单元,请使用 DRL 文件中的 unit 关键字。单元 声明必须立即遵循 package 声明,并包含 DRL 文件中规则所属的类名称。

DRL 文件中的规则单元声明示例

package org.mypackage.myunit
unit AdultUnit

rule Adult
  when
    $p : Person(age >= adultAge) from persons
  then
    System.out.println($p.getName() + " is adult and greater than " + adultAge);
end
Copy to Clipboard Toggle word wrap

警告

不要混合同一 KIE 基础中的规则单元和没有规则单元。将两个规则组合在 KIE 基础中会导致编译错误。

您还可以使用 OOPath 表示法以更方便的方式重写相同的模式,如下例所示:

使用 OOPath 表示法的 DRL 文件中的规则单元声明示例

package org.mypackage.myunit
unit AdultUnit

rule Adult
  when
    $p : /persons[age >= adultAge]
  then
    System.out.println($p.getName() + " is adult and greater than " + adultAge);
end
Copy to Clipboard Toggle word wrap

注意

OOPath 是 XPath 的面向对象的语法扩展,旨在浏览 DRL 规则条件限制中的对象图形。OOPath 使用 XPath 中的紧凑表示法来导航相关元素,同时处理集合和过滤约束,对对象图形特别有用。

在本例中,规则条件中的任何匹配事实都从规则单元类的 DataSource 定义中定义的个人数据源检索。规则条件和操作使用 adultAge 变量的方式与在 DRL 文件级别上定义全局变量的方式相同。

要执行 KIE 基础中定义的一个或多个规则单元,请创建一个新的 RuleUnitExecutor 类,从相关数据源创建规则单元,并运行规则单元执行器:

规则单元执行示例

// Create a `RuleUnitExecutor` class and bind it to the KIE base:
KieBase kbase = kieContainer.getKieBase();
RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase );

// Create the `AdultUnit` rule unit using the `persons` data source and run the executor:
RuleUnit adultUnit = new AdultUnit(persons, 18);
executor.run( adultUnit );
Copy to Clipboard Toggle word wrap

规则由 RuleUnitExecutor 类执行。RuleUnitExecutor 类创建 KIE 会话,并将所需的 DataSource 对象添加到这些会话中,然后根据作为参数传递给 run () 方法的 RuleUnit 执行规则。

当相关的 Person 事实插入到个人数据源中时,执行代码会生成以下输出:

规则单元执行输出示例

org.mypackage.myunit.AdultUnit started.
Jane is adult and greater than 18
John is adult and greater than 18
org.mypackage.myunit.AdultUnit ended.
Copy to Clipboard Toggle word wrap

您可以将规则单元变量注册到 executor 中,而是明确创建规则单元实例,并传递给 executor 运行的规则单元类,然后 executor 创建规则单元的实例。然后,您可以在运行规则单元前根据需要设置 DataSource 定义和其他变量。

带有注册变量的替代规则单元执行选项

executor.bindVariable( "persons", persons );
        .bindVariable( "adultAge", 18 );
executor.run( AdultUnit.class );
Copy to Clipboard Toggle word wrap

您传递给 RuleUnitExecutor.bindVariable () 方法的名称在运行时将变量绑定到规则单元类的字段,其名称具有相同名称。在上例中,RuleUnitExecutor 将数据源插入到新规则单元中,其数据源绑定到 "persons" 名称,并将绑定到 String "adultAge" 的值插入到带有 AdultUnit 类中对应名称的字段中。

要覆盖此默认 variable-binding 行为,请使用 @UnitVar 注释为规则单元类的每个字段明确定义逻辑绑定名称。例如,以下类中的字段绑定使用其他名称重新定义:

使用 @UnitVar修改变量绑定名称的代码示例

package org.mypackage.myunit;

public static class AdultUnit implements RuleUnit {
    @UnitVar("minAge")
    private int adultAge = 18;

    @UnitVar("data")
    private DataSource<Person> persons;
}
Copy to Clipboard Toggle word wrap

然后,您可以使用这些替代名称将变量绑定到 executor,并运行规则单元:

使用修改后的变量名称执行规则单元示例

executor.bindVariable( "data", persons );
        .bindVariable( "minAge", 18 );
executor.run( AdultUnit.class );
Copy to Clipboard Toggle word wrap

您可以使用 run () 方法(等同于 KIE 会话上的 fireAllRules () )或使用 runUntilHalt () 方法(等同于 KIE 会话上的 fireUntilHalt () )在 被动模式下执行 规则单元。默认情况下,决策引擎以 被动模式 运行,仅当用户或应用程序明确调用 run () (或针对标准规则的 fireAllRules () )时评估规则单元。如果用户或应用程序调用 runUntilHalt () 用于规则单元(或 fireUntilHalt () 用于标准规则),则决策引擎以 活跃模式 启动并评估规则单元,直到用户或应用程序显式调用 halt ()

如果您使用 runUntilHalt () 方法,请在单独的执行线程上调用方法以避免阻塞主线程:

在单独的线程中使用 runUntilHalt () 执行规则单元示例

new Thread( () -> executor.runUntilHalt( adultUnit ) ).start();
Copy to Clipboard Toggle word wrap

80.7.1. 规则单元的数据源

规则单元数据源是由给定规则单元处理的数据来源,代表决策引擎用于评估规则单元的入口点。规则单元可以有零个或更多数据源,在规则单元中声明的每个 DataSource 定义可以对应于规则单元 executor 的不同入口点。多个规则单元可以共享一个数据源,但每个规则单元都必须使用插入同一对象的不同入口点。

您可以在规则单元类中使用固定数据集合创建 DataSource 定义,如下例所示:

数据源定义示例

DataSource<Person> persons = DataSource.create( new Person( "John", 42 ),
                                                new Person( "Jane", 44 ),
                                                new Person( "Sally", 4 ) );
Copy to Clipboard Toggle word wrap

因为数据源代表规则单元的入口点,您可以在规则单元中插入、更新或删除事实:

在规则单元中插入、修改和删除事实的代码示例

// Insert a fact:
Person john = new Person( "John", 42 );
FactHandle johnFh = persons.insert( john );

// Modify the fact and optionally specify modified properties (for property reactivity):
john.setAge( 43 );
persons.update( johnFh, john, "age" );

// Delete the fact:
persons.delete( johnFh );
Copy to Clipboard Toggle word wrap

80.7.2. 规则单元执行控制

当您要协调规则执行时,规则单元很有用,以便执行一个规则单元会触发另一个规则单元的开始等。

为便于规则单元执行控制,决策引擎支持以下规则单元方法,您可以在 DRL 规则操作中使用,以协调规则单元的执行:

  • drools.run () :触发指定规则单元类的执行。这个方法有意中断规则单元的执行,并激活其他指定的规则单元。
  • drools.guard (): Prevents (guards)指定的规则单元类执行,直到满足关联的规则条件。这个方法声明性地调度其他指定规则单元的执行。当决策引擎为保护规则中的条件至少生成一个匹配项时,保护的规则单元被视为活动状态。规则单元可以包含多个保护规则。

作为 drools.run () 方法的示例,考虑每个都属于指定的规则单元的以下 DRL 规则:NotAdult 规则使用 drools.run (AdultUnit.class) 方法触发 AdultUnit 规则单元的执行:

使用 drools.run ()控制执行的 DRL 规则示例

package org.mypackage.myunit
unit AdultUnit

rule Adult
  when
    Person(age >= 18, $name : name) from persons
  then
    System.out.println($name + " is adult");
end
Copy to Clipboard Toggle word wrap

package org.mypackage.myunit
unit NotAdultUnit

rule NotAdult
  when
    $p : Person(age < 18, $name : name) from persons
  then
    System.out.println($name + " is NOT adult");
    modify($p) { setAge(18); }
    drools.run( AdultUnit.class );
end
Copy to Clipboard Toggle word wrap

这个示例还使用从这些规则构建的 KIE 基础创建的 RuleUnitExecutor 类,以及绑定到它的 人员的 DataSource 定义:

规则执行器和数据源定义示例

RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase );
DataSource<Person> persons = executor.newDataSource( "persons",
                                                     new Person( "John", 42 ),
                                                     new Person( "Jane", 44 ),
                                                     new Person( "Sally", 4 ) );
Copy to Clipboard Toggle word wrap

在本例中,示例直接从 RuleUnitExecutor 类创建 DataSource 定义,并将其绑定到单个语句中的 "persons" 变量。

当相关的 Person 事实插入到个人数据源中时,执行代码会生成以下输出:

规则单元执行输出示例

Sally is NOT adult
John is adult
Jane is adult
Sally is adult
Copy to Clipboard Toggle word wrap

NotAdult 规则在评估人员 "Sly" 时检测到匹配项,这些人员为 18 年。然后,该规则将自己的年龄修改为 18,并使用 drools.run (AdultUnit.class) 方法触发 AdultUnit 规则单元的执行。AdultUnit 规则单元包含一个规则,现在可以为 DataSource 定义中的所有 3 个人执行该规则。

作为 drools.guard () 方法的示例,请考虑以下 BoxOffice 类和 BoxOfficeUnit 规则单元类:

BoxOffice 类示例

public class BoxOffice {
    private boolean open;

    public BoxOffice( boolean open ) {
        this.open = open;
    }

    public boolean isOpen() {
        return open;
    }

    public void setOpen( boolean open ) {
        this.open = open;
    }
}
Copy to Clipboard Toggle word wrap

BoxOfficeUnit 规则单元类示例

public class BoxOfficeUnit implements RuleUnit {
    private DataSource<BoxOffice> boxOffices;

    public DataSource<BoxOffice> getBoxOffices() {
        return boxOffices;
    }
}
Copy to Clipboard Toggle word wrap

这个示例还使用以下 TicketIssuerUnit 规则单元类来保持活动的办公室框票据,只要至少有一个框办公室处于打开状态。这个规则单元 使用 人员和 票据 数据源定义

TicketIssuerUnit 规则单元类示例

public class TicketIssuerUnit implements RuleUnit {
    private DataSource<Person> persons;
    private DataSource<AdultTicket> tickets;

    private List<String> results;

    public TicketIssuerUnit() { }

    public TicketIssuerUnit( DataSource<Person> persons, DataSource<AdultTicket> tickets ) {
        this.persons = persons;
        this.tickets = tickets;
    }

    public DataSource<Person> getPersons() {
        return persons;
    }

    public DataSource<AdultTicket> getTickets() {
        return tickets;
    }

    public List<String> getResults() {
        return results;
    }
}
Copy to Clipboard Toggle word wrap

BoxOfficeUnit 规则单元包含一个 BoxOfficeIsOpen DRL 规则,它使用 drools.guard (TicketIssuerUnit.class) 方法来保护执行 TicketIssuerUnit 规则单元,如以下 DRL 规则示例所示:

使用 drools.guard ()控制执行的 DRL 规则示例

package org.mypackage.myunit;
unit TicketIssuerUnit;

rule IssueAdultTicket when
    $p: /persons[ age >= 18 ]
then
    tickets.insert(new AdultTicket($p));
end
rule RegisterAdultTicket when
    $t: /tickets
then
    results.add( $t.getPerson().getName() );
end
Copy to Clipboard Toggle word wrap

package org.mypackage.myunit;
unit BoxOfficeUnit;

rule BoxOfficeIsOpen
  when
    $box: /boxOffices[ open ]
  then
    drools.guard( TicketIssuerUnit.class );
end
Copy to Clipboard Toggle word wrap

在这个示例中,只要至少有一个框办公室 处于打开状态,则保护的 TicketIssuerUnit 规则单元处于活动状态并分发事件票据。如果没有更多框 处于打开状态,则防止执行 guarded TicketIssuerUnit 规则单元。

以下示例类演示了一个更加完整的办公室场景:

框办公室情境类示例

DataSource<Person> persons = executor.newDataSource( "persons" );
DataSource<BoxOffice> boxOffices = executor.newDataSource( "boxOffices" );
DataSource<AdultTicket> tickets = executor.newDataSource( "tickets" );

List<String> list = new ArrayList<>();
executor.bindVariable( "results", list );

// Two box offices are open:
BoxOffice office1 = new BoxOffice(true);
FactHandle officeFH1 = boxOffices.insert( office1 );
BoxOffice office2 = new BoxOffice(true);
FactHandle officeFH2 = boxOffices.insert( office2 );

persons.insert(new Person("John", 40));

// Execute `BoxOfficeIsOpen` rule, run `TicketIssuerUnit` rule unit, and execute `RegisterAdultTicket` rule:
executor.run(BoxOfficeUnit.class);

assertEquals( 1, list.size() );
assertEquals( "John", list.get(0) );
list.clear();

persons.insert(new Person("Matteo", 30));

// Execute `RegisterAdultTicket` rule:
executor.run(BoxOfficeUnit.class);

assertEquals( 1, list.size() );
assertEquals( "Matteo", list.get(0) );
list.clear();

// One box office is closed, the other is open:
office1.setOpen(false);
boxOffices.update(officeFH1, office1);
persons.insert(new Person("Mark", 35));
executor.run(BoxOfficeUnit.class);

assertEquals( 1, list.size() );
assertEquals( "Mark", list.get(0) );
list.clear();

// All box offices are closed:
office2.setOpen(false);
boxOffices.update(officeFH2, office2); // Guarding rule is no longer true.
persons.insert(new Person("Edson", 35));
executor.run(BoxOfficeUnit.class); // No execution

assertEquals( 0, list.size() );
Copy to Clipboard Toggle word wrap

80.7.3. 规则单元身份冲突

在带有保护的规则单元的规则单元执行场景中,规则可以保护多个规则单元,并同时保护规则单元,然后由多个规则激活。对于这些双向保护场景,规则单元必须具有明确定义的身份以避免身份冲突。

默认情况下,规则单元的身份是规则单元类名称,并被视为 RuleUnitExecutor 的单例类。这种识别行为在 RuleUnit 接口的 getUnitIdentity () 默认方法中进行编码:

RuleUnit 接口中的默认身份方法

default Identity getUnitIdentity() {
    return new Identity( getClass() );
}
Copy to Clipboard Toggle word wrap

在某些情况下,您可能需要覆盖此默认识别行为,以避免规则单元间的冲突。

例如,以下 RuleUnit 类包含一个接受任何类型的对象的 DataSource 定义:

unit 0 规则单元类示例

public class Unit0 implements RuleUnit {
    private DataSource<Object> input;

    public DataSource<Object> getInput() {
        return input;
    }
}
Copy to Clipboard Toggle word wrap

此规则单元包含以下 DRL 规则,该规则根据两个条件保护另一个规则单元(在 OOPath 表示法中):

规则单元中的 GuardAgeCheck DRL 规则示例

package org.mypackage.myunit
unit Unit0

rule GuardAgeCheck
  when
    $i: /input#Integer
    $s: /input#String
  then
    drools.guard( new AgeCheckUnit($i) );
    drools.guard( new AgeCheckUnit($s.length()) );
end
Copy to Clipboard Toggle word wrap

已保护的 AgeCheckUnit 规则单元验证一组人的年龄。AgeCheckUnit 包含要检查的人员的 DataSource 定义、它验证的 minAge 变量,以及收集结果的列表:

AgeCheckUnit 规则单元示例

public class AgeCheckUnit implements RuleUnit {
    private final int minAge;
    private DataSource<Person> persons;
    private List<String> results;

    public AgeCheckUnit( int minAge ) {
        this.minAge = minAge;
    }

    public DataSource<Person> getPersons() {
        return persons;
    }

    public int getMinAge() {
        return minAge;
    }

    public List<String> getResults() {
        return results;
    }
}
Copy to Clipboard Toggle word wrap

AgeCheckUnit 规则单元包含以下 DRL 规则,该规则执行数据源中的 人员 验证:

规则单元中的 CheckAge DRL 规则示例

package org.mypackage.myunit
unit AgeCheckUnit

rule CheckAge
  when
    $p : /persons{ age > minAge }
  then
    results.add($p.getName() + ">" + minAge);
end
Copy to Clipboard Toggle word wrap

这个示例创建了一个 RuleUnitExecutor 类,将类绑定到包含这两个规则单元的 KIE 基础,并为同一规则单元创建两个 DataSource 定义:

executor 和数据源定义示例

RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase );

DataSource<Object> input = executor.newDataSource( "input" );
DataSource<Person> persons = executor.newDataSource( "persons",
                                                     new Person( "John", 42 ),
                                                     new Person( "Sally", 4 ) );

List<String> results = new ArrayList<>();
executor.bindVariable( "results", results );
Copy to Clipboard Toggle word wrap

现在,您可以将一些对象插入到输入数据源中,并执行 unit 0 规则单元:

使用插入的对象执行规则单元示例

ds.insert("test");
ds.insert(3);
ds.insert(4);
executor.run(Unit0.class);
Copy to Clipboard Toggle word wrap

执行的结果列表示例

[Sally>3, John>3]
Copy to Clipboard Toggle word wrap

在本例中,名为 AgeCheckUnit 的规则单元被视为单例类,然后仅执行一次,minAge 变量设为 3。插入到输入数据源中的字符串 "test" 和 Integer 4 也可以触发第二个执行,并将 minAge 变量设置为 4。但是,第二个执行不会发生,因为已评估具有相同身份的另一个规则单元。

要解决此规则单元身份冲突,请覆盖 AgeCheckUnit 类的 getUnitIdentity () 方法,以便在规则单元身份中包含 minAge 变量:

修改 AgeCheckUnit 规则单元,以覆盖 getUnitIdentity () 方法

public class AgeCheckUnit implements RuleUnit {

    ...

    @Override
    public Identity getUnitIdentity() {
        return new Identity(getClass(), minAge);
    }
}
Copy to Clipboard Toggle word wrap

使用这个覆盖,前面的规则单元执行示例会生成以下输出:

执行修改的规则单元的结果列表示例

[John>4, Sally>3, John>3]
Copy to Clipboard Toggle word wrap

现在,minAge 设置为 34 的规则单元被视为两个不同的规则单元,两者都被执行。

返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat