80.7. DRL 规则集中的规则单元
规则单元是数据源、全局变量和 DRL 规则组,适用于特定目的。您可以使用规则单元将规则集分区为较小的单元,将不同的数据源绑定到这些单元,然后执行单个单元。规则单元是规则组 DRL 属性的增强替代方案,如规则电缆组或执行控制的激活组。
当您要协调规则执行时,规则单元很有用,以便一个规则单元的完整执行会触发另一个规则单元的开始等。例如,假设您有一组用于数据增强的规则、处理该数据的一组规则,以及从处理的数据中提取输出的另一组规则。如果将这些规则集添加到三个不同的规则单元中,您可以协调这些规则单元,以便完成第一单元的执行会触发第二个单元的开始,第二个单元的完整执行会触发第三个单元的开始。
要定义规则单元,实施 RuleUnit
接口,如下例所示:
规则单元类示例
在本例中,人员
是类型为 Person
的事实来源。规则单元数据源是由给定规则单元处理的数据来源,代表决策引擎用于评估规则单元的入口点。adultAge
全局变量可从属于此规则单元的所有规则访问。最后两种方法是规则单元生命周期的一部分,由决策引擎调用。
决策引擎支持以下可选生命周期方法用于规则单元:
方法 | 调用的时间 |
---|---|
| 规则单元执行开始 |
| 规则单元执行结束 |
|
规则单元执行暂停(仅用于 |
|
规则单元执行会被恢复(仅用于 |
| 规则单元中规则的结果会触发执行不同的规则单元 |
您可以在规则单元中添加一个或多个规则。默认情况下,DRL 文件中的所有规则都与遵循 DRL 文件名命名约定的规则单元自动关联。如果 DRL 文件位于同一软件包中,且名称与实现 RuleUnit
接口的类相同,则 DRL 文件中的所有规则都会隐式属于该规则单元。例如,org.mypackage.myunit
软件包中的 AdultUnit.drl
文件中的所有规则都自动是规则单元 org.mypackage.myunit.AdultUnit
的一部分。
要覆盖此命名惯例并明确声明 DRL 文件中的规则单元,请使用 DRL 文件中的 unit
关键字。单元
声明必须立即遵循 package 声明,并包含 DRL 文件中规则所属的类名称。
DRL 文件中的规则单元声明示例
不要混合同一 KIE 基础中的规则单元和没有规则单元。将两个规则组合在 KIE 基础中会导致编译错误。
您还可以使用 OOPath 表示法以更方便的方式重写相同的模式,如下例所示:
使用 OOPath 表示法的 DRL 文件中的规则单元声明示例
OOPath 是 XPath 的面向对象的语法扩展,旨在浏览 DRL 规则条件限制中的对象图形。OOPath 使用 XPath 中的紧凑表示法来导航相关元素,同时处理集合和过滤约束,对对象图形特别有用。
在本例中,规则条件中的任何匹配事实都从规则单元类的 DataSource
定义中定义的个人数据源检索。规则条件和操作使用
adultAge
变量的方式与在 DRL 文件级别上定义全局变量的方式相同。
要执行 KIE 基础中定义的一个或多个规则单元,请创建一个新的 RuleUnitExecutor
类,从相关数据源创建规则单元,并运行规则单元执行器:
规则单元执行示例
规则由 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.
org.mypackage.myunit.AdultUnit started.
Jane is adult and greater than 18
John is adult and greater than 18
org.mypackage.myunit.AdultUnit ended.
您可以将规则单元变量注册到 executor 中,而是明确创建规则单元实例,并传递给 executor 运行的规则单元类,然后 executor 创建规则单元的实例。然后,您可以在运行规则单元前根据需要设置 DataSource
定义和其他变量。
带有注册变量的替代规则单元执行选项
executor.bindVariable( "persons", persons ); .bindVariable( "adultAge", 18 ); executor.run( AdultUnit.class );
executor.bindVariable( "persons", persons );
.bindVariable( "adultAge", 18 );
executor.run( AdultUnit.class );
您传递给 RuleUnitExecutor.bindVariable
() 方法的名称在运行时将变量绑定到规则单元类的字段,其名称具有相同名称。在上例中,RuleUnitExecutor
将数据源插入到新规则单元中,其数据源绑定到 "persons"
名称,并将绑定到 String "adultAge"
的值插入到带有 AdultUnit
类中对应名称的字段中。
要覆盖此默认 variable-binding 行为,请使用 @UnitVar
注释为规则单元类的每个字段明确定义逻辑绑定名称。例如,以下类中的字段绑定使用其他名称重新定义:
使用 @UnitVar
修改变量绑定名称的代码示例
然后,您可以使用这些替代名称将变量绑定到 executor,并运行规则单元:
使用修改后的变量名称执行规则单元示例
executor.bindVariable( "data", persons ); .bindVariable( "minAge", 18 ); executor.run( AdultUnit.class );
executor.bindVariable( "data", persons );
.bindVariable( "minAge", 18 );
executor.run( AdultUnit.class );
您可以使用 run ()
方法(等同于 KIE 会话上的 fireAllRules ()
)或使用 runUntilHalt ()
方法(等同于 KIE 会话上的 fireUntilHalt ()
)在 被动模式下执行 规则单元。默认情况下,决策引擎以 被动模式 运行,仅当用户或应用程序明确调用 run ()
(或针对标准规则的 fireAllRules ()
)时评估规则单元。如果用户或应用程序调用 runUntilHalt ()
用于规则单元(或 fireUntilHalt ()
用于标准规则),则决策引擎以 活跃模式 启动并评估规则单元,直到用户或应用程序显式调用 halt ()
。
如果您使用 runUntilHalt ()
方法,请在单独的执行线程上调用方法以避免阻塞主线程:
在单独的线程中使用 runUntilHalt ()
执行规则单元示例
new Thread( () -> executor.runUntilHalt( adultUnit ) ).start();
new Thread( () -> executor.runUntilHalt( adultUnit ) ).start();
80.7.1. 规则单元的数据源 复制链接链接已复制到粘贴板!
规则单元数据源是由给定规则单元处理的数据来源,代表决策引擎用于评估规则单元的入口点。规则单元可以有零个或更多数据源,在规则单元中声明的每个 DataSource
定义可以对应于规则单元 executor 的不同入口点。多个规则单元可以共享一个数据源,但每个规则单元都必须使用插入同一对象的不同入口点。
您可以在规则单元类中使用固定数据集合创建 DataSource
定义,如下例所示:
数据源定义示例
DataSource<Person> persons = DataSource.create( new Person( "John", 42 ), new Person( "Jane", 44 ), new Person( "Sally", 4 ) );
DataSource<Person> persons = DataSource.create( new Person( "John", 42 ),
new Person( "Jane", 44 ),
new Person( "Sally", 4 ) );
因为数据源代表规则单元的入口点,您可以在规则单元中插入、更新或删除事实:
在规则单元中插入、修改和删除事实的代码示例
80.7.2. 规则单元执行控制 复制链接链接已复制到粘贴板!
当您要协调规则执行时,规则单元很有用,以便执行一个规则单元会触发另一个规则单元的开始等。
为便于规则单元执行控制,决策引擎支持以下规则单元方法,您可以在 DRL 规则操作中使用,以协调规则单元的执行:
-
drools.run ()
:触发指定规则单元类的执行。这个方法有意中断规则单元的执行,并激活其他指定的规则单元。 -
drools.guard ()
: Prevents (guards)指定的规则单元类执行,直到满足关联的规则条件。这个方法声明性地调度其他指定规则单元的执行。当决策引擎为保护规则中的条件至少生成一个匹配项时,保护的规则单元被视为活动状态。规则单元可以包含多个保护规则。
作为 drools.run
() 方法的示例,考虑每个都属于指定的规则单元的以下 DRL 规则:NotAdult
规则使用 drools.run (AdultUnit.class)
方法触发 AdultUnit
规则单元的执行:
使用 drools.run ()
控制执行的 DRL 规则示例
这个示例还使用从这些规则构建的 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 ) );
RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase );
DataSource<Person> persons = executor.newDataSource( "persons",
new Person( "John", 42 ),
new Person( "Jane", 44 ),
new Person( "Sally", 4 ) );
在本例中,示例直接从 RuleUnitExecutor
类创建 DataSource
定义,并将其绑定到单个语句中的 "persons"
变量。
当相关的 Person
事实插入到个人数据源中时,执行代码会生成以下输出:
规则单元执行输出示例
Sally is NOT adult John is adult Jane is adult Sally is adult
Sally is NOT adult
John is adult
Jane is adult
Sally is adult
NotAdult
规则在评估人员 "Sly"
时检测到匹配项,这些人员为 18 年。然后,该规则将自己的年龄修改为 18
,并使用 drools.run (AdultUnit.class)
方法触发 AdultUnit
规则单元的执行。AdultUnit
规则单元包含一个规则,现在可以为 DataSource
定义中的所有 3 个人执行该规则。
作为 drools.guard
() 方法的示例,请考虑以下 BoxOffice
类和 BoxOfficeUnit
规则单元类:
BoxOffice
类示例
BoxOfficeUnit
规则单元类示例
这个示例还使用以下 TicketIssuerUnit
规则单元类来保持活动的办公室框票据,只要至少有一个框办公室处于打开状态。这个规则单元 使用
人员和 票据
:
的
数据源定义
TicketIssuerUnit
规则单元类示例
BoxOfficeUnit
规则单元包含一个 BoxOfficeIsOpen
DRL 规则,它使用 drools.guard (TicketIssuerUnit.class)
方法来保护执行 TicketIssuerUnit
规则单元,如以下 DRL 规则示例所示:
使用 drools.guard ()
控制执行的 DRL 规则示例
在这个示例中,只要至少有一个框办公室 处于打开状态
,则保护的 TicketIssuerUnit
规则单元处于活动状态并分发事件票据。如果没有更多框 处于打开状态
,则防止执行 guarded TicketIssuerUnit
规则单元。
以下示例类演示了一个更加完整的办公室场景:
框办公室情境类示例
80.7.3. 规则单元身份冲突 复制链接链接已复制到粘贴板!
在带有保护的规则单元的规则单元执行场景中,规则可以保护多个规则单元,并同时保护规则单元,然后由多个规则激活。对于这些双向保护场景,规则单元必须具有明确定义的身份以避免身份冲突。
默认情况下,规则单元的身份是规则单元类名称,并被视为 RuleUnitExecutor
的单例类。这种识别行为在 RuleUnit
接口的 getUnitIdentity ()
默认方法中进行编码:
RuleUnit
接口中的默认身份方法
default Identity getUnitIdentity() { return new Identity( getClass() ); }
default Identity getUnitIdentity() {
return new Identity( getClass() );
}
在某些情况下,您可能需要覆盖此默认识别行为,以避免规则单元间的冲突。
例如,以下 RuleUnit
类包含一个接受任何类型的对象的 DataSource
定义:
unit 0
规则单元类示例
此规则单元包含以下 DRL 规则,该规则根据两个条件保护另一个规则单元(在 OOPath 表示法中):
规则单元中的 GuardAgeCheck
DRL 规则示例
已保护的 AgeCheckUnit
规则单元验证一组人的年龄。AgeCheckUnit
包含要检查的人员的 DataSource
定义、它验证的 minAge
变量,以及收集结果的列表:
AgeCheckUnit
规则单元示例
AgeCheckUnit
规则单元包含以下 DRL 规则,该规则执行数据源中的 人员
验证:
规则单元中的 CheckAge
DRL 规则示例
这个示例创建了一个 RuleUnitExecutor
类,将类绑定到包含这两个规则单元的 KIE 基础,并为同一规则单元创建两个 DataSource
定义:
executor 和数据源定义示例
现在,您可以将一些对象插入到输入数据源中,并执行 unit 0
规则单元:
使用插入的对象执行规则单元示例
ds.insert("test"); ds.insert(3); ds.insert(4); executor.run(Unit0.class);
ds.insert("test");
ds.insert(3);
ds.insert(4);
executor.run(Unit0.class);
执行的结果列表示例
[Sally>3, John>3]
[Sally>3, John>3]
在本例中,名为 AgeCheckUnit
的规则单元被视为单例类,然后仅执行一次,minAge
变量设为 3
。插入到输入数据源中的字符串 "test"
和 Integer 4
也可以触发第二个执行,并将 minAge
变量设置为 4
。但是,第二个执行不会发生,因为已评估具有相同身份的另一个规则单元。
要解决此规则单元身份冲突,请覆盖 AgeCheckUnit
类的 getUnitIdentity ()
方法,以便在规则单元身份中包含 minAge
变量:
修改 AgeCheckUnit
规则单元,以覆盖 getUnitIdentity ()
方法
使用这个覆盖,前面的规则单元执行示例会生成以下输出:
执行修改的规则单元的结果列表示例
[John>4, Sally>3, John>3]
[John>4, Sally>3, John>3]
现在,minAge
设置为 3
和 4
的规则单元被视为两个不同的规则单元,两者都被执行。