首页 > 解决方案 > 版本从 v7.20 更改为 v7.21 后,drools 中的无限递归

问题描述

当 drools-compiler 发生版本更改时7.20.0.Final7.21.0.Final某些规则会递归循环。

github中的代码:

工作版本

递归循环版本

工作和循环版本之间的变化

更多细节

当我触发一条规则时,其部分修改了已在该部分then中检查的事实:when

rule "rule 1.1"
  when
    $sampleDomain: SampleDomain(instanceVariable2 == "Value of instance variable")
  then
    System.out.println("Rule 1.1 fired");
    modify($sampleDomain){
            setInstanceVariable1(3)
    }
end

它不会递归循环。

但是当我调用另一个从另一个类调用静态函数的规则时:

rule "rule 1.2"
  when
    $sampleDomain: SampleDomain(CoreUtils.anotherFunction())
  then
    System.out.println("Rule 1.2 fired");
    modify($sampleDomain){
            setInstanceVariable1(3)
    }
end

它递归地循环。

具有静态功能的类是

import com.drool_issue.domain.SampleDomain;

public class CoreUtils {

    public static boolean anotherFunction() {
        System.out.println("anotherFunction() inside CoreUtils");
        return true;
    }

    public static boolean anotherFunction(SampleDomain sampleDomain) {
        System.out.println("anotherFunction(SampleDomain sampleDomain) inside CoreUtils");
        return true;
    }

}

我的域文件是:

public class SampleDomain {
    private int instanceVariable1;
    private String instanceVariable2;
    private int instanceVariable3;


    public int getInstanceVariable1() {
        return instanceVariable1;
    }
    public void setInstanceVariable1(int instanceVariable1) {
        this.instanceVariable1 = instanceVariable1;
    }
    public String getInstanceVariable2() {
        return instanceVariable2;
    }
    public void setInstanceVariable2(String instanceVariable2) {
        this.instanceVariable2 = instanceVariable2;
    }
    public int getInstanceVariable3() {
        return instanceVariable3;
    }
    public void setInstanceVariable3(int instanceVariable3) {
        this.instanceVariable3 = instanceVariable3;
    }



}

这仅在版本从 更改为 后7.20.0.Final引起7.21.0.Final。任何猜测可能是什么问题?

当我进一步研究这个问题时,我也看到了这一点。

当我们在SampleDomain类中添加两个函数时,即

    public boolean anotherFunction() {
        return true;
    }

    public boolean anotherFunction(SampleDomain sampleDomain) {
        return true;
    }

并在规则中使用它,例如:

rule "rule 1.4"
  when
    $sampleDomain: SampleDomain(anotherFunction())
  then
    System.out.println("Rule 1.4 fired");
    modify($sampleDomain){
            setInstanceVariable1(3)
    }
end

rule "rule 1.5"
  when
    $sampleDomain: SampleDomain(anotherFunction($sampleDomain))
  then
    System.out.println("Rule 1.5 fired");
    modify($sampleDomain){
            setInstanceVariable3(4)
    }
end

这些也递归循环。

github中的代码:

使用非静态方法时的递归循环

工作版和以上版本之间的变化

此外,当任何静态方法变为非静态时,即使在规则中指定了静态方法,也会调用域类中的方法。

这里要注意的代码部分是:

调用静态方法的规则。

另一个规则也称为静态方法。

从以前静态的函数中删除了静态访问修饰符。

github中的代码:

删除函数的静态修饰符时的奇怪行为。

工作版和以上版本之间的变化

所有这些都是在, 7.20.0.Finalie和之后的版本中引起的7.21.0.Final7.22.0.Final7.23.0.Final

标签: drools

解决方案


基于属性反应性的过滤不能应用于外部函数,因为我们不知道外部函数(静态方法)在内部做什么。

详细说明:让我们从属性反应性开始;每当我们在规则的结果中使用修改或更新关键字时,我们都会通知引擎过滤相似对象类型的规则应该再次重新评估该对象。默认情况下,此重新评估发生在整个对象上。即只要对象的某个属性发生变化,规则就会将其视为新对象进行匹配。当我们不希望针对某些更改重新评估规则时,这可能会导致一些问题。在这些情况下,循环控制机制(例如无循环和活动锁定)可能会有所帮助。但是,如果我们希望规则只控制某些属性的更改,我们需要编写非常复杂的条件。此外,如果模型在未来针对大型规则库发生变化,您可能需要修改很多规则以避免不希望的规则重新执行。幸运的是,Drools 提供了一个功能,可以让引擎解决这个问题。它允许规则编写者定义 bean 的属性,如果它们在工作内存中更新,则应对其进行监视。此功能可以在规则中使用的数据模型(Java 类或声明的类型)中定义,称为属性反应 bean。

要使用此功能,我们首先需要使用 Property Reactive 注释标记我们将在规则中使用的类型。这个注解让引擎知道,只要将这种类型的对象添加到工作内存中,就需要对其更改进行特殊过滤。这个注解可以添加到 Java 类(在类级别)或声明的类型(就在第一行之后,定义类型名称),如下所示,在 Java 类中:

@PropertyReactive public class Customer {     ... }

在声明的类型中:

    declare PropertyReactiveOrder        
        @propertyReactive
        discount: Discount
        totalItems: Integer
        total: Double    
    end 

我们还可以通过添加PropertySpecificOption.ALWAYS到 builder 选项使所有类型的属性反应。

注意:属性响应式 bean 更改将仅使用 modify 关键字通知给规则引擎。update 关键字将无法区分正在更改的 bean 的属性。

Drools 5.4 中引入了属性反应性,但由于从正确性和性能的角度来看,使用此功能被认为是一种很好的做法,因此在 Drools 7.0 中默认启用它。

现在再次回到我们的问题,尽管属性反应性已经发生了巨大的变化,它就像一个细粒度的属性变化监听器,允许 Drools 更多地了解我们规则的 RHS 内部发生的事情,或者在至少在 modify() 操作中。这些功能仍然是它的黑匣子,从而产生了这个问题。


推荐阅读