Drools入门之规则流

普通运行方式

  1. 引入依赖

    <dependency>
        <groupId>org.jbpm</groupId>
        <artifactId>jbpm-test</artifactId>
        <version>${drools.version}</version> <!-- 我使用的是7.10.0.Final -->
    </dependency>
    
  2. 在rules/isFlow下新建ruleFlow.bpmn文件。

  3. 打开properites窗口,修改规则流属性。

    修改id为:No1_drools;package为:rules.isFlow

    ID在当前规则库中的逻辑路径是唯一的,这点与规则文件rule name是一样的,package指规则流程文件的逻辑路径,与规则文件中的package功能一样。

    image-20220728153248154

  4. 创建规则文件ruleFlow.drl,目录为rules/isFlow

    package rules.isFlow
    
    rule "rule folw example1"
    dialect "mvel" 
    ruleflow-group "No1_drools_RuleFlow"
    when
    	eval(true)
    then
    	System.out.println("输出第一个规则流")
    end
    
    
    rule "rule no folw example1"
    dialect "mvel" 
    when
    	eval(true)
    then
    	System.out.println("没有使用规则流")
    end
    
  5. 配置任务元素组件,通过元素组件画出如下图所示的效果,并选中规则任务元素组件,设置“RuleFlowGroup:No1_drools_RuleFlow”选项,该值要与规则文件中的ruleflow-group保持一致。

    image-20220728154116578

  6. 修改kmodule.xml配置文件

    <kbase name="flow" packages="rules.isFlow">
        <ksession name="testFlow"/>
    </kbase>
    
  7. 创建测试方法

    @Test
    public void testFlow() {
        KieServices kieService = KieServices.Factory.get();
        KieContainer kieContainer = kieService.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("testFlow");
    
        kieSession.startProcess("No1_drools"); //启动流程
    
        kieSession.fireAllRules();
        kieSession.dispose();
    }
    
  8. 执行结果

    输出第一个规则流
    没有使用规则流
    

    若没有加kieSession.startProcess("No1_drools");这行代码,就不会执行第一个规则。

API调用方式

@Test
public void testIsFlowAPI() {
    Resource bpmn = ResourceFactory.newClassPathResource("rules/isFlow/ruleFlow.bpmn",this.getClass());
    Resource drl = ResourceFactory.newClassPathResource("rules/isFlow/ruleFlow.drl",this.getClass());
    KieHelper helper = new KieHelper();
    helper.addResource(bpmn,ResourceType.BPMN2);
    helper.addResource(drl,ResourceType.DRL);
    KieSession kieSession = helper.build().newKieSession();
    kieSession.startProcess("No1_drools"); //启动流程

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

总结

  1. 当流程文件元素组件只有一个,多个规则体属性ruleflow-group相同,且流程文件元素组件的RuleFlowGroup属性与规则体的属性相同时,则会执行满足条件的规则。
  2. 当流程文件有多个规则元素组件,该规则元素组件的RuleFlowGroup相同,多个规则体属性ruleflow-group相同,且流程文件元素组件的RuleFlowGroup属性与规则体的属性相同时,则会执行满足条件的规则,并且只会执行一遍,不会因规则元素组件的增多而执行多次规则。但凡是规则流参数相同的规则体都会受salience的影响,这点需要格外注意。
  3. 当流程文件有多个规则元素组件,并且该规则元素组件的RuleFlowGroup是不相同的,多个规则体属性ruleflow-group与规则元素组件一一对应时,则会执行满足条件的规则。就算在规则体中设置了salience属性,规则元素组件也不会受其影响而改变规则的执行顺序。
  4. 流程文件中规则元素组件的RuleFlowGroup值与规则文件中规则体属性ruleflow-group没有匹配时,则规则不会被执行。

综上所述,使用规则流属性时必须添加流程文件,规则体中规则流属性要与流程文件规则元素组件的RuleFlowGroup一一对应。规则流的执行会受其他属性的影响,并且还会受流程文件元素组件顺序的影响。

规则流中的Java代码

静态方法调用
  1. 创建FlowToJava.java文件

    package com.rulesHello;
    
    public class FlowToJava {
    	public static void flowToJava1() {
    		System.out.println("输出第一个通过流程调用java方法");
    	}
    }
    
  2. 创建规则文件flowToJava.drl,目录为rules/isFlow/flowJava

    package rules.isFlow.flowJava
    
    rule "规则流调用java例子1"
    ruleflow-group "flowToJava"
        when
        
        then
            System.out.println(drools.getRule().getName());
    end
    
  3. 创建流程文件flowToJava.bpmn

    image-20220728164430690

  4. 设置规则流属性

    规则流id:flowToJava,package:rules.isFlow.flowJava

    Rule TaskK组件RuleFlowGroup:flowToJava

  5. 设置Script元素组件,设置如下图所示

    image-20220728164841019

    image-20220728164916863

  6. 配置kmodule.xml

    <kbase name="flowToJava" packages="rules.isFlow.flowJava">
        <ksession name="testFlowToJava"/>
    </kbase>
    
  7. 测试方法

    @Test
    public void testFlowToJava() {
        KieServices kieService = KieServices.Factory.get();
        KieContainer kieContainer = kieService.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("testFlowToJava");
    
        kieSession.startProcess("flowToJava"); //启动流程
    
        kieSession.fireAllRules();
        kieSession.dispose();
    }
    
  8. 运行结果

    输出第一个通过流程调用java方法
    规则流调用java例子1
    
非静态方法调用
  1. 创建FlowToJavaTwo.java文件,目录为comTwo/flow,其内容为:

    public class FlowToJavaTwo {
    	private static final FlowToJavaTwo FLOW = new FlowToJavaTwo();
    	
    	public static FlowToJavaTwo getFlow() {
    		return FLOW;
    	}
    	
    	public void flowToJava02() {
    		System.out.println("第二种通过单例模式调用Java方法");
    	}
    }
    
  2. 修改规则流

    image-20220729104833620

  3. 修改非静态的action信息

    image-20220729104927504

  4. 运行测试方法

  5. 输出日志

    输出第一个通过流程调用java方法
    第二种通过单例模式调用Java方法
    规则流调用java例子1
    

传参

若要调用有参数的方法,如何传参呢?

  1. 首先创建一个带有参数的方法

    public class FlowToJava {
    	public static void flowToJava02(String value) {
    		System.out.println("输出传递的参数:"+value);
    	}
    }
    
  2. 修改规则流中调用的方法

    image-20220729110741047

  3. 点击规则流文件的空白部分,修改Variables属性(Name要和上面的参数名一致)

    image-20220729110941474

  4. 修改测试方法(map的key要和上面设置的一致)

    @Test
    public void testFlowToJava() {
        KieServices kieService = KieServices.Factory.get();
        KieContainer kieContainer = kieService.getKieClasspathContainer();
        KieSession kieSession = kieContainer.newKieSession("testFlowToJava");
    
        Map<String,Object> map = new HashMap<String, Object>();
    
        map.put("value", "我是传递的参数");
    
        kieSession.startProcess("flowToJava",map); //启动流程
    
        kieSession.fireAllRules();
        kieSession.dispose();
    }
    
  5. 运行测试方法,输出结果

    输出传递的参数:我是传递的参数
    第二种通过单例模式调用Java方法
    规则流调用java例子1
    

规则流中的网关

网关可以分为两种:分离网关和合并网关。为了适应官方文档上的方式,先将bpmn的展现方式做一个变化。

image-20220729161018665

image-20220729161147245

网关分为分离网关和合并网关两种,这两种网关都可以设置不同的type,它们都有AND和XOR,但分离网关多一种OR。

介绍完这两种网关的基本知识,下面对其类型分别进行测试。创建separation.bpmn文件。

image-20220801144033728

其中Action组件设置为System.out.println()输出。输出的内容为显示为Action组件的名称。

分离网关与合并网关type都为AND

分离网关的的type设置为AND,合并网关的type设置为AND。

创建测试方法

@Test
public void testFlowGeteway() {
    Resource bpmn = ResourceFactory.newClassPathResource("rules/isFlow/gateway/separation.bpmn",this.getClass());
    KieHelper helper = new KieHelper();
    helper.addResource(bpmn,ResourceType.BPMN2);
    KieSession kieSession = helper.build().newKieSession();
    kieSession.startProcess("gateway"); //启动流程

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

执行方法,输出结果为

start
分支三为True
分支二为False
分支一为True
end

当所有网关都为AND时,流程元素组件都将会被访问。

合并网关type为XOR

将合并网关type改为XOR。执行结果

start
分支三为True
end

当分离网关为AND,合并网关为XOR时,只会执行其中一个流向,这点与规则属性activation-group很相似。

分离网关的type为XOR

将分离网关的type设置成XOR,多了一个Constraints可选参数,单击该参数进行配置

image-20220801145844639

从上图中可以看到,将分离网关设置在XOR后,是可以在每一条连接线进行设置,并且设置该连接线又会分为以下几种情况。

  1. name:表示连接线的名称。
  2. priority:表示连接线的优先级,值越小优先级越高。
  3. Always true:表示永远为true。
  4. Type:表示以什么方式进行判断结果,这里使用rule类型。
  5. Textual Editor:表示输入代码表达式的区域。
  6. Imports:表示可以引用其他类或方法。
  7. Globals:表示可以设置规则全局变量。

介绍完其作用后,分别对这3个分支进行设置:

分支一 name:f1,priority:1,规则:eval(true)

分支二 name:f2,priority:2,规则:eval(false)

分支三 name:f3,priority:3,规则:eval(true)

合并网关为AND

将合并网关设置为AND,执行测试方法,执行结果为:

start
分支一为True

总结:当分离网关为XOR,合并网关为AND时,无论分离网关是全True,还是有一个为True时,都不会执行到元素组件End。

合并网关为XOR

将合并网关的type设置成XOR

start
分支一为True
end

总结:当分离网关为XOR,合并网关为XOR时,分支中有两个为True,一个为False的情况下,只会有一个被执行,这是受优先级设置的影响。这证明只有分支为True的才会被执行并有且只有其中一个流向被执行。如果设置分支都为False,则会报如下图所示的错误。

image-20220802103652052

分离网关为OR
合并网关为AND

执行结果

start
分支一为True
分支三为True

将所有的分支都设置为eavl(True),执行结果

start
分支一为True
分支二为False
分支三为True
end

总结:当分离网关为OR,合并网关为AND时,只有全部为True的情况下,所有流向才会被汇总,这也符合AND的逻辑与功能,如果分离网关所有分支为False时,则会报错。

合并网关为XOR
start
分支一为True
end

将分支1和2都设置为eavl(False)

start
分支三为True
end

总结:当分离网关为OR,合并网关为XOR时,只会执行分支为True的流向,并且只会汇总其中一个,这是受优先级影响。


Drools入门之规则流
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/drools-rule-flow
作者
卑微幻想家
发布于
2022-08-02
许可协议