语法基础

package

package定义在规则文件的首行,是规则文件的三大模块之一。

package表示规则的逻辑路径。建议在定义的时候和物理路径相同。但这并不是必须的。可以不同。不过强烈建议。

package包含 import、global、funcation、query、rule、EOF。

rule是规则文件的核心。前面学习的规则属性就是rule中的内容。

在前面的示例中,我们配置过kmodule.xml这样的一个文件,在这个文件中KieBase元素设置了packages路径,来表示当前路径下的所有规则文件,像规则文件、决策表、领域语言文件等等都会被加入到规则库中。但是当前路径下子文件夹的规则文件并没有包含在当前的规则库中。

package参数实际上是一个命名空间,没有去关联文件或者文件夹。因此,可以有多个规则目录为规则库构建源组合规则。有一个顶级的package配置。所有的规则都在其控制之下。

虽然生命在不同名称下的资源不可能合并成一个包,但是单个规则库可以用多个包来构建它。

packages可以设置多个路径,通过逗号来分割。

创建规则内容:

 1 package rules.isPackage
 2 rule "testRuleNameOnly"
 3     when
 4         eval(true);
 5     then
 6         System.out.println("testRuleNameOnly say hello");
 7 end
 8 rule "testRuleNameOnly"
 9     when
10         eval(true);
11     then
12         System.out.println("testRuleNameOnly say hello");
13 end

在配置文件中继续添加:

  <kbase name="isPackage" packages="rules.isPackage">
        <ksession name="isPackge"/>
    </kbase>

看下运行结果:

2019-08-07 22:58:12 [ERROR]org.drools.....KieProject - Unable to build KieBaseModel:isPackage
[8,0]: Duplicate rule name: testRuleNameOnly
Rule Compilation error : [Rule name='testRuleNameOnly']
    rules/isPackage/Rule_testRuleNameOnly670538843.java (3:109) : The type Rule_testRuleNameOnly670538843 is already defined



java.lang.RuntimeException: Error while creating KieBase[Message [id=1, kieBase=isPackage, level=ERROR, path=D:JavaDevworkspaceDroolsProjectBaseProject	argetclasses
ules.isPackagepackage.drl, line=8, column=0
   text=Duplicate rule name: testRuleNameOnly], Message [id=2, kieBase=isPackage, level=ERROR, path=D:JavaDevworkspaceDroolsProjectBaseProject	argetclasses
ules.isPackagepackage.drl, line=8, column=0
   text=Rule Compilation error The type Rule_testRuleNameOnly670538843 is already defined]]

    at org.drools.compiler.kie.builder.impl.KieContainerImpl.getKieBase(KieContainerImpl.java:363)
    at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:519)
    at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:487)
    at com.packages.RulePackage.RulePackage(RulePackage.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

很是明显的一个错误,提示规则文件代码rule名称重复了。修改名称后就可以正常运行。这个时候再添加一个规则文件,内容如下:

package rules.isPackage2
rule "testRuleNameOnly"
    when
        eval(true);
    then
        System.out.println("testRuleNameOnly say hello");
end

和之前的例子中区别就是package参数是不相同的。继续看下结果:

2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
testRuleNameOnly say hello
testRuleNameOnly say hello
testRuleNameOnly say hello
2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了3条规则
2019-08-07 23:02:41 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

从结果中可以看出,同一个物理路径下的规则相关文件都会被加载到规则苦衷,不同规则文件中不同的package会影响规则名称的定义。

继续创建一个规则文件,测试一下子目录下的规则文件是不是会被加载。

创建规则内容:

package rules.isPackage.package2;
rule "testRuleNameOnly"
    when
        eval(true);
    then
        System.out.println("testRuleNameOnly say hello");
end

看下结果:

2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
testRuleNameOnly say hello
testRuleNameOnly say hello
testRuleNameOnly say hello
2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了3条规则
2019-08-07 23:06:32 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

结果是没有任何变化的。说明了规则库不会去加载子目录下的规则相关文件。

global

全局变量,由关键字global class name组成。 class是很随意的,能够为规则提供操作数据或者服务等功能。特别实在规则RHS部分中使用程序提供的服务,例如,添加日志,发送邮件,操作数据表等。

global全局变量与Fact对象有区别,不会因为值的改变而影响到规则的再次激活。

创建规则内容如下:

package rules.isPackage
rule "testRuleNameOnly"
    when
        eval(true);
    then
        System.out.println("testRuleNameOnly say hello");
end
rule "testRuleNameOnly2"
    when
        eval(true);
    then
        System.out.println("testRuleNameOnly say hello");
end

创建调用代码:

   @Test
    public void testActivationGroup() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isGlobal");
        ks.setGlobal("count", 2019);
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

看下执行结果:

1 globalupdate1 count:2019
2 globalupdate1 count:10
3 globalupdate2 count:2019
4 2019-08-08 23:19:55 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
5 2019-08-08 23:19:55 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
6 总共执行了2条规则

结论:当前规则体中修改的值包装类的全局变量只会影响到当前规则体。

如果多个package使用相同标识声明的全局变量,那么他们的类型必须是相同的,并且它们所有引用都是相同的全局变量。

创建规则:

package rules.isGlobal;

global java.lang.Boolean count;

rule "global2"
    when
    then
        System.out.println("global2 count:" + count);
end

执行结果会报错如下:

ule Compilation error : [Rule name='globalupdate1']
    rules/isGlobal/Rule_globalupdate1174728079.java (8:445) : Type mismatch: cannot convert from int to Boolean

重新编写文件内容如下:

package rules.isGlobal;

//global java.lang.Boolean count;
global java.lang.Integer count

rule "global2"
    when
    then
        System.out.println("global2 count:" + count);
end

再次执行后,不会报错。

global功能:

1.全局变量定义成常量或者包装类型时,这个值对于整个规则而言是不变的。但是如果在同一段规则代码中改变了global值,那么其作用只是针对这段规则代码而言,使用的则是被修改后的global值。对其他规则代码或者元素节点中的global都不会有任何的影响。也可以这样去说,他是当前规则代码或其他元素节点中的global副本。规则内部不会影响全局的使用。

global变量作为全局变量放在规则中,虽然说全局变量的值是不可以变化的。但是并不建议用于数据之间的共享。因为,针对不同类型全局变量中的内容也可能会发生变化。

2.全局变量定义为集合类或者JavaBean时,在规则体RHS部分中进行修改,则规则库或Java代码中的值都是会发生变化的。这说明了全局变量并非不可变的值。如果在多个地方使用并修改了全局变量,可能就会导致结果非常奇怪了。

query查询

以query开头,以end结束。其中包含query name,查询参数是可以选择的,多个参数以逗号为分隔符。需要注意的是查询是一种条件匹配的方式,所以它只包含LHS部分。所以不需要when 或者then。如何判断条件是否正确,可以通过Java代码进行获取。查询中的name在当前规则库的逻辑路径下是唯一的,和规则名称的约束是一样的。

创建规则示例:

package rules.isQuery;
import com.entity.Person;

query "person age is 30"
    person:Person(age == 30);
end 

添加配置文件:

    <kbase name="isQuery" packages="rules.isQuery">
        <ksession name="isQuery"/>
    </kbase>

创建测试调用的代码:

    @Test
    public void testQuery() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isQuery");
        Person person1 = new Person("张三", "35");
        Person person2 = new Person("李四", "30");
        Person person3 = new Person("王五", "50");
        ks.insert(person1);
        ks.insert(person2);
        ks.insert(person3);
        QueryResults queryResults = ks.getQueryResults("person age is 30");
        for (QueryResultsRow q : queryResults) {
            Person person = (Person) q.get("person");
            System.out.println("输出符合查询条件的对象的name是:" + person.getName());
        }
        ks.dispose();
    }

看一下输出的结果:

2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE
输出符合查询条件的对象的name是:李四
2019-08-09 22:27:08 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

是符合预期的。规则文件中没有使用任何的规则体,query的目的只是为了判断insert到规则中的Fact对象是否满足条件。query非常简单,判断Person属性是否满足age==30,如果满足,就返回一个集合。

另外,query是可以添加参数的。

创建规则内容如下:

package rules.isQuery;
import com.entity.Person;

query "person age is 30 and name is 张小三" (String $name)
    person:Person(name == $name, age == 30);
end

创建调用端的测试代码示例:

@Test
    public void testQuery2() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isQuery");
        Person person1 = new Person("张三", "35");
        Person person2 = new Person("李四", "30");
        Person person3 = new Person("王五", "50");
        Person person4 = new Person("张小三", "30");
        ks.insert(person1);
        ks.insert(person2);
        ks.insert(person3);
        ks.insert(person4);
        Object[] objects = new Object[]{"张小三"};
        QueryResults queryResults = ks.getQueryResults("person age is 30 and name is 张小三", objects);
        for (QueryResultsRow q : queryResults) {
            Person person = (Person) q.get("person");
            System.out.println("输出符合查询条件的对象的name是:" + person.getName());
        }
        ks.dispose();
    }

看下运行的结果:

2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now EXECUTING_TASK
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was EXECUTING_TASK is now INACTIVE
输出符合查询条件的对象的name是:张小三
2019-08-09 22:38:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

符合预期。

function

function函数的写法是和JavaScript中的函数类似的,规则函数是Java类方法的一种变形,它的参数是非必填的信息。返回值也是非必填的。一个规则使用函数的好处是可以在同一个地方保持所有的逻辑,同一个逻辑路径下的函数是一个全局的函数,如果规则函数发生变化,就意味着所有调用该函数的规则体都发生了变化。

规则中的函数有两种形式,一种是import,可以去引用Java中的静态方法,另一种需要在规则中添加关键字function.

创建规则体内容:

package rules.isFunction;

rule "function"
    when
    then
        function01();
        System.out.println("函数function02()的返回值" + function02());
        function03("张小三");
        System.out.println("函数function04()的返回值" + function04("李小四"));
    end

function void function01() {
    System.out.println("一个无参无返回值的函数");
}

function String function02() {
    System.out.println("一个无参有返回值的函数");
    return "Hello";
}

function void function03(String name) {
    System.out.println("一个有参无返回值的函数,输出入参是:" + name);
}

function String function04(String name) {
    System.out.println("一个有参没有返回值的函数,输出入参是" + name);
    return name;
}

在kmodule.xml文件中继续添加节点:

    <kbase name="isFunction" packages="rules.isFunction">
        <ksession name="isFunction"/>
    </kbase>

创建调用规则的Java客户端代码:

    @Test
    public void testFunction() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isFunction");
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

运行结果如下:

2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
一个无参无返回值的函数
一个无参有返回值的函数
函数function02()的返回值Hello
一个有参无返回值的函数,输出入参是:张小三
一个有参没有返回值的函数,输出入参是李小四
函数function04()的返回值李小四
2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了1条规则
2019-08-10 20:49:20 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

function函数和一个Java方法并没有什么区别。

最后来验证下同一个逻辑路径下或者不同路径下函数的访问范围。

1.同一个逻辑路径下

创建规则文件:

package rules.isFunction;

rule "function2"
when
then
    function01();
    System.out.println("函数function02()的返回值" + function02());
    function03("张小小");
    System.out.println("函数function04()的返回值" + function04("李小小"));
end

看下调用的结果:

一个无参无返回值的函数
一个无参有返回值的函数
函数function02()的返回值Hello
一个有参无返回值的函数,输出入参是:张小小
一个有参没有返回值的函数,输出入参是李小小
函数function04()的返回值李小小
一个无参无返回值的函数
一个无参有返回值的函数
函数function02()的返回值Hello
一个有参无返回值的函数,输出入参是:张小三
一个有参没有返回值的函数,输出入参是李小四
函数function04()的返回值李小四

证明了function函数在同逻辑路径下是全局性的。

相反,如果不在同一逻辑路径下,是不可用的。

function函数的第一种形式,是通过Java静态方法的方式。

创建FunctionStatic.java文件


package com.rules.isFunction;

public class FunctionStatic {

public static void testStatic1() {
System.out.println("一个无参无返回值的Java静态方法");
}

public static String testStatic2() {
System.out.println("一个无参有返回值的Java静态方法");
return "Hello";
}

public static void testStatic3(String name) {
System.out.println("一个有参有返回值的Java静态方法,参数是:" + name);
}

public static String testStatic4(String name) {
System.out.println("一个有参有返回值的Java静态方法, 参数是" + name);
return name;
}
}

调用代码:

package rules.isFunction;
import function com.rules.isFunction.FunctionStatic.testStatic1;
import function com.rules.isFunction.FunctionStatic.testStatic2;
import function com.rules.isFunction.FunctionStatic.testStatic3;
import function com.rules.isFunction.FunctionStatic.testStatic4;

rule "function3"
    when
    then
        testStatic1();
        System.out.println("testStatic2()的返回值" + testStatic2());
        testStatic3("张小三");
        System.out.println("testStatic4()的返回值" + testStatic4("李小四"));
    end

结果如下:

2019-08-10 21:11:00 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
一个无参无返回值的Java静态方法
一个无参有返回值的Java静态方法
testStatic2()的返回值Hello
一个有参有返回值的Java静态方法,参数是:张小三
一个有参有返回值的Java静态方法, 参数是李小四

 declare声明

declare在规则引擎中的功能主要有两个:一个是声明新类型,二是声明元数据类型。

1.声明新类型 和JavaBean功能一样,但是方式比JavaBean简单。在之前的例子里面,在规则中操作Fact对象都是通过Java代码insert到规则中进行处理。但是有时候,我们不希望去编写JavaBean。在这个时候,使用declare再好不过。

2.声明元数据类型,fact对象中包含了一些特性,这些特性称之为类元信息。如果需要当前的属性长度是固定的,那么就可以在属性声明前添加元数据。一般元数据用于在查询中,复杂的事件处理和属性字段的约束最多。

创建规则内容:

package rules.isDeclare;

declare Person
    name:String
    age:int
end

rule "declareInsert"
when
then
    insert(new Person("张三", 20));
end

rule "declareTest"
when
    $p:Person(name=="张三")
then
    System.out.println("使用declare测试insert后进行操作");
end

在kmodule.xml中添加节点:

    <kbase name="isDeclare" packages="rules.isDeclare">
        <ksession name="isDeclare"/>
    </kbase>

创建测试代码:

   @Test
    public void testDeclare() {
        KieServices kieServices = KieServices.Factory.get();
        KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
        KieSession session = kieClasspathContainer.newKieSession("isDeclare");
        int count = session.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        session.dispose();
    }

运行结果:

2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
使用declare测试insert后进行操作
2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了2条规则
2019-08-11 17:39:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

例子中声明了一个名称是Person的新的Fact类型。定义声明属性类型可以是任意的Java有效类型,并且包括自定义创建的任何其他类,甚至可以是当前规则中已有的声明类型。

在声明一个新的事实类型时,Drools会在编译时生成实现表示事实类型的一个Java类的字节码。生成的Java类,将是一个一对一JavaBean类型定义的映射。

因为生成的类是一个简单的Java类,相当于在当前规则中import一个对象,所以在同一个逻辑路径下的规则体中都是可以使用的。和全局变量,函数查询的共享范围都是一致的。

如果在规则文件中声明的类型和import引用的类名相同时,系统会报错。

创建规则内容:

package rules.isDeclare;
import com.entity.Person;

declare Person
    name:String
    age:int
end

运行测试代码结果:

New declaration of com.entity.Person does not include field className
New declaration of com.entity.Person redeclared field age : 
existing : java.lang.String vs declared : int
New declaration of com.entity.Person can't declare a different set of fields 
existing : [age, className, name]
declared : [age, name]
diff : [--className]
Rule Compilation error : [Rule name='declareInsert']
    rules/isDeclare/Rule_declareInsert725960459.java (7:390) : The constructor Person(String, int) is undefined



java.lang.RuntimeException: Error while creating KieBase[Message [id=1, kieBase=isDeclare, level=ERROR, path=D:JavaDevworkspaceDroolsProjectBaseProject	argetclasses
ules.isDeclaredeclare.drl, line=4, column=0
   text=New declaration of com.entity.Person does not include field className], Message [id=2, kieBase=isDeclare, level=ERROR, path=D:JavaDevworkspaceDroolsProjectBaseProject	argetclasses
ules.isDeclaredeclare.drl, line=4, column=0
   text=New declaration of com.entity.Person redeclared field age : 
existing : java.lang.String vs declared : int], Message [id=3, kieBase=isDeclare, level=ERROR, path=D:JavaDevworkspaceDroolsProjectBaseProject	argetclasses
ules.isDeclaredeclare.drl, line=4, column=0
   text=New declaration of com.entity.Person can't declare a different set of fields 
existing : [age, className, name]
declared : [age, name]
diff : [--className]], Message [id=4, kieBase=isDeclare, level=ERROR, path=D:JavaDevworkspaceDroolsProjectBaseProject	argetclasses
ules.isDeclaredeclare.drl, line=9, column=0
   text=Rule Compilation error The constructor Person(String, int) is undefined]]

    at org.drools.compiler.kie.builder.impl.KieContainerImpl.getKieBase(KieContainerImpl.java:363)
    at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:519)
    at org.drools.compiler.kie.builder.impl.KieContainerImpl.newKieSession(KieContainerImpl.java:487)
    at com.com.rulesDeclare.RulesDeclare.testDeclare(RulesDeclare.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

将声明类型中的属性删除,再次执行,就不会报错。原因是规则文件编译冲突,规则并不知道自己到底需要用哪一种引用类型,所以只能是系统报错。

声明类型中还有一个和Java继承相似得功能。也是通过关键字extends。

创建规则内容:

package rules.isDeclare

declare PersonEx extends com.entity.Person
    type : String
end

rule "declareInsertExt"
when 
then 
    PersonEx p = new PersonEx();
    p.setType("1");
    p.setAge("20");
    p.setName("张三");
    insert(p);
end

rule "declareTestExtends"
when 
    $p:PersonEx(name=="张三")
then 
    System.out.println("hello" + $p.getName());
end

看下运行的结果:

2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
使用declare测试insert后进行操作
hello张三
2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了4条规则
2019-08-11 18:05:17 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

规则when

规则文件中处理业务的核心在于规则体的灵活使用,简称规则,它的书写规范,和Java语言是相似的。LHS部分是when与then之间的语法。规则属性判断过程,只有在规则LHS中匹配结果是true时,才认为规则成立,才会跳转到RHS部分。如果说LHS部分是空,就表示这个规则永远都是true。当工作空间中Fact对象发生变化时,这个规则呢可能会再次被激活,我们可以使用之前学习过的no-loop属性得到解决。

pattern匹配模式中,pattern表示一种约束条件。看一下它的组织结构:

patternBinding:patternType ( constraints )

匹配模式由零个或者多个约束组成,并且有一个可以选择的模式绑定,在最简单的格式中,没有内部的约束条件,通过一个给定类型的事实就可以进行模式匹配了。要注意下,不必是一些事实对象的实际类,模式可以引用子类,接口,函数等。

引用匹配的对象,可以为一个模式绑定变量,例如:$c,这个前缀符号$其实不是必须的,但是在复杂的规则中非常有用,可以有助于去区别变量和字段。例如:$p:Person(age==30);

模式中的括号内部是所有匹配的地方,约束可以是一个字段,或者是一个约束组,也可以是一个其内部的计算。约束可以使用, &&  || 符号分割。

和Java中是一样的,比较运算符是有优先级的。进行与或非等运算时要注意短路机制。

对象属性有3种类型的限制,单值限制,复合值限制和多限制。单值限制就是上面所说个那种样子,复合限制类似于数据库中查询语句中的in和not in。

1.复合值限制in not in

创建规则内容:

 1 package rules.isIn
 2 import com.entity.Person;
 3 import com.entity.School;
 4 
 5 rule "inTest"
 6 when
 7     School($cn:className)
 8     Person(className in (5, 6, $cn))
 9 then
10     System.out.println("in test");
11 end
12 
13 rule "notinTest"
14 when
15     School($cn:className)
16     Person(className not in (5, 6, $cn))
17 then
18     System.out.println("not in test");
19 end

修改配置文件,继续添加节点:

    <kbase name="isIn" packages="rules.isIn">
        <ksession name="isIn"/>
    </kbase>

创建测试调用代码:

 1 @Test
 2     public void testIn() {
 3 
 4         KieServices kss = KieServices.Factory.get();
 5         KieContainer kc = kss.getKieClasspathContainer();
 6         KieSession ks = kc.newKieSession("isIn");
 7 
 8         Person person = new Person();
 9         person.setClassName("3");
10         ks.insert(person);
11         School school = new School();
12         school.setClassName("3");
13         ks.insert(school);
14         Person person2 = new Person();
15         person2.setClassName("4");
16         ks.insert(person2);
17         int count = ks.fireAllRules();
18         System.out.println("总共执行了" + count + "条规则");
19         ks.dispose();
20 
21 
22     }

最后看下运行的效果:

1 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
2 in test
3 not in test
4 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
5 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
6 总共执行了2条规则
7 2019-08-12 23:32:14 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

2.条件元素eval

格式:eval(expression)

条件元素eval是在最开始的示例中就列举过的。它可以代表任何语义的代码。并且返回一个boolean类型,也可以绑定在规则LHS中的变量,函数或者直接写常量进行比较。但是在实际编码的过程中,尽量少用eval作为比较符,因为会导致引擎的性能问题。

3.条件元素not

格式:not(conditionalElement)

条件元素not是判断在工作内存中是否还存在某一个值,当not ec成立时就表示当前工作内存中不存在ec,可以认为是一定没有这个值。

创建规则体内容:

package rules.isNot
import com.entity.Person;

rule "testNot"
   when
       not Person();
   then
       System.out.println("测试Person一定不再工作内存中");
end

rule "testNotx2"
   when
       not(not Person());
   then
       System.out.println("测试Person一定在工作内存中");
end

在配置文件中节点中添加:

    <kbase name="isNot" packages="rules.isNot">
        <ksession name="isNot"/>
    </kbase>

测试代码:

    @Test
    public void testIn() {

        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isIn");

        Person person = new Person();
        person.setClassName("3");
        ks.insert(person);
        School school = new School();
        school.setClassName("3");
        ks.insert(school);
        Person person2 = new Person();
        person2.setClassName("4");
        ks.insert(person2);
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();


    }

看下运行结果:

2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
测试Person一定在工作内存中
2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了1条规则
2019-08-13 22:31:02 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

符合预期。

4.条件元素 exists 

格式:exists(conditionalElement)

条件元素exists的功能是和not相反的,指的是工作内存中是否存在某一个东西,可以认为是至少有一个。

创建规则内容:

package rules.isExists
import com.entity.Person;

rule "testExists"
   when
       exists Person();
   then
       System.out.println("测试Person一定在工作内存中");
end

rule "testExistsx2"
   when
       not (exists Person());
   then
       System.out.println("测试Person一定不在工作内存中");
end

创建客户端调用代码:

   @Test
    public void testExists() {

        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isNot");

        Person person = new Person();
        person.setClassName("1");
        person.setAge("35");
        ks.insert(person);

        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();


    }

看下运行结果:

2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
测试Person一定在工作内存中
2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了1条规则
2019-08-13 22:39:43 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

符合预期。

条件元素from

结构:pattern from expression

条件元素from很有意思,可以指定任意的资源,用于LHS部分的数据匹配,也可以用来对集合进行遍历。还可以用来对Java服务进行访问并且对结果进行遍历。还有global全局变量也可以提供Java服务。

创建规则内容:

package rules.isFrom
import com.entity.Person
import com.entity.School

rule "testFrom"
when
    $p:Person($ps:school)
    $s:School(className=="1") from $ps
then
    System.out.println("test from");
end

修改配置文件:

<kbase name="isFrom" packages="rules.isFrom">
        <ksession name="isFrom"/>
    </kbase>

测试代码:

@Test
    public void testFrom() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isFrom");
        Person person = new Person();
        person.setAge("30");
        person.setName("张三");
        person.setSchool(new School("1"));
        ks.insert(person);
        ks.insert(new School("2"));
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

运行结果:

2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
test from
2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了1条规则
2019-08-14 22:37:36 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

上面的规则内容其实可以用下面的这种写法来替代:

rule "testFrom2"
when
    $p:Person()
    $s:School(className=="1") from $p.school
then
    System.out.println("test from2");
end

条件元素from支持对象源,会返回一个对象集合,在这种情况下,from将会遍历集合中所有的对象,并分别匹配它们中每一个对象值。

创建规则内容:

rule "testFrom3"
when
    $s:School()
    $p:Person(className=="1") from $s.classNameList
then 
    System.out.println("testFrom3" + $p.getName());
end

创建调用端:

@Test
    public void testFromList() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isFrom");
        Person person = new Person();
        person.setName("张三");
        person.setClassName("1");
        Person person2 = new Person();
        person2.setName("李四");
        person2.setClassName("1");
        Person person3 = new Person();
        person3.setName("王五");
        person3.setClassName("2");
        Person person4 = new Person();
        person4.setName("赵六");
        person4.setClassName("1");
        School school = new School();
        List<Person> classNameList = new ArrayList();
        classNameList.add(person);
        classNameList.add(person2);
        classNameList.add(person3);
        classNameList.add(person4);
        school.setClassNameList(classNameList);
        ks.insert(school);
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

运行后结果:

2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
testFrom3赵六
testFrom3李四
testFrom3张三
2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了3条规则
2019-08-14 22:56:37 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

此外,要注意在使用属性时,特别是与lock-on-active规则属性联合使用时,因为这个属性可以产生不一样的效果。

最后,from不仅可以在对象,集合中应用,还可以在函数中应用。

条件元素collect

pattern 'from' 'collect' (pattern from collect accumulate)

条件元素collect需要结合from元素来使用。from是用来遍历的,而 from collect是用来汇总的。collect后的参数中还可以匹配 遍历 收集 统计的功能。

创建规则体内容:

package rules.isCollect
import com.entity.Person
import com.entity.School
import java.util.ArrayList

rule "test from collect"
when
    $al: ArrayList() from collect($p:Person(className=="1"))
then
    System.out.println("test from collect array list size:" + $al.size()); 
end

rule "test from collect pattern"
when
    $al: ArrayList(size>=3) from collect($p:Person(className=="1"))
then
    System.out.println("test from collect array list size:" + $al.size()); 
end

修改kmodule.xml配置文件:

<kbase name="isCollect" packages="rules.isCollect">
        <ksession name="isCollect"/>
    </kbase>

创建测试代码:

@Test
    public void testFromCollect() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isCollect");
        Person person = new Person();
        person.setName("张三");
        person.setClassName("1");
        ks.insert(person);
        Person person2 = new Person();
        person2.setName("李四");
        person2.setClassName("1");
        ks.insert(person2);
        Person person3 = new Person();
        person3.setName("王五");
        person3.setClassName("2");
        ks.insert(person3);
        Person person4 = new Person();
        person4.setName("赵流");
        person4.setClassName("1");
        ks.insert(person4);
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

结果:

2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
test from collect array list size:3
test from collect array list size:3
2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了2条规则
2019-08-15 23:38:12 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

collect参数中使用from。添加规则体:

rule "test from collect from"
when
    $s:School()
    
    $al: ArrayList(size>=3) from collect($p:Person(className=="1") from $s.classNameList)
then
    System.out.println("test from collect array list size:" + $al.size());
end

创建测试端调用代码:

@Test
    public void testFromCollectFrom() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isCollect");
        Person person = new Person();
        person.setName("张三");
        person.setClassName("1");

        Person person2 = new Person();
        person2.setName("李四");
        person2.setClassName("1");

        Person person3 = new Person();
        person3.setName("王五");
        person3.setClassName("2");

        Person person4 = new Person();
        person4.setName("赵流");
        person4.setClassName("1");

        School school = new School();
        List classNameList= new ArrayList();
        classNameList.add(person);
        classNameList.add(person2);
        classNameList.add(person3);
        classNameList.add(person4);
        school.setClassNameList(classNameList);
        ks.insert(school);

        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

运行结果:

2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
test from collect array list size:0
test from collect array list size:3
2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了2条规则
2019-08-15 23:43:16 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

符合预期。

◆ 条件元素规则的继承

条件元素规则继承与java相似。因为Drools是以规则引擎是基于java开发的开源技术。在编写规则时,大部分的操作都离不开使用Java脚本对一类fact对象进行操作。同时Drools中语法也存在继承的逻辑关系,其目的是为了减少相同代码的出现,从而方便代码的优化和管理。

创建规则内容:

package rules.isExtends
import com.entity.Person
import com.entity.School

rule "test extends No1"
when
    $p:Person(name == "张小三")
then
    System.out.println("输出张小三");
end

rule "test extends No2" extends "test extends No1"
when
    $s:School(className == "1")
then
    System.out.println("输出张三和1");
end

修改配置文件:

<kbase name="isExtends" packages="rules.isExtends">
        <ksession name="isExtends"/>
    </kbase>

创建测试代码:

   @Test
    public void testExtends() {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("isExtends");
        Person person = new Person();
        person.setName("张小三");
        School school = new School();
        school.setClassName("1");
        ks.insert(person);
        ks.insert(school);
        int count = ks.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ks.dispose();
    }

运行结果:符合预期

2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
输出张小三
输出张三和1
2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了2条规则
2019-08-16 08:45:11 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

 kmodule配置说明

kmodule是非常重要的配置文件。它关系到规则库是否可以成功的创建以及KieSession的正确使用。将kmodule.xml文件放到src/main/resources/META-INF/文件夹下,内容如下:

    <kbase name="rule" packages="rules.rulesHello">
        <ksession name="testHelloWorld"/>
    </kbase>

name: 

必填,值类型String

用于从KieContainer检索这个KieBase的名称。是规则库的名称,又是唯一的强制属性。最好规范命名,见名知意。

packages:

非必填,值类型 String

默认的情况下,加载资源文件夹中所有Drools相关文件,这个属性目的是将当前路径下的规则文件在KieBase中编译。仅仅限于指定的软件包路径下的所有文件。这个值是可以多个的。中间用逗号分隔即可。如果路径是子文件,就必须用小数点进行区别,类似Java包。

includes:

非必填,值类型

指一个KieBase被另一个KieBase2的配置中设置了includes='KieBase1',那么KieBase2就有了KieBase1所有资源了,当然这个值也可以是多个,中间任然使用逗号分隔开,有点类似继承的意思。

default:

非必填,值类型 true/false

用来表示当前KieBase是否是次模块的默认值,因此可以从KieContainer中创建,而不会传递任何名称。每个模块中最多只能有一个默认的KieBase,默认值是false.

equalsBehavior

非必填 值类型 Indentity/equality 

将新Fact事实对象插入到工作内存中时,定义了Drools的操作,使用了身份属性,那么它会创建一个新的FactHandle,除非相同的对象不存在与工作内存中,而只有新插入的对象不存在与工作内从中,而只有新插入的对象与已存在的事实对象不相等时才会相同。

eventProcessingMode

非必填 值类型 Cloud/stream

当以云模式编译时,KieBase将事实视为正常事实,而在流模式下可以对其进行实践推理。

declarativeAgenda

菲必填 值类型Disabled enabled

定义声明议程是否启用。

KieSession属性声明

name 

必填 值类型 string

KieSession的名称。用于从KieContainer中获取KieSession。唯一一个强制必填的属性。用于指定操作规则的会话。

type

非必填 值类型 stateful/stateless 指当前KieSession的类型是有状态还是没有状态的,默认不指定为有状态的。

default

非必填  值类型 true/false 

定义这个KieSession是否是个模块的默认值,如果这个值是true,就可以从KieContainer中创建,而不会传递任何名称。在每个模块中,每个类型最多可以有一个默认的KieSession,且是有状态。

clockType

非必填 realtime/seudo

定义事件时间戳是有系统时钟还是由应用程序控制的seudo时钟确定。这个时钟对于单元测试时间规则特别有用。

beiefSystem

非必填 simple/tms/defeasible

定义KieSession使用的beiefSystem的类型。

Spring整合Drools

1.在pom.xml文件中引入相应的jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>DroolsProject</artifactId>
        <groupId>com.sunlei</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>DroolsSpring</artifactId>

    <properties>
        <spring.version>4.2.6.RELEASE</spring.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-spring</artifactId>
            <version>7.10.0.Final</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>







    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>
                    ${project.basedir} /src/main/resources
                </directory>
            </resource>
        </resources>

    </build>


</project>

2.创建spring 配置文件,注意其中的节点配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:kie="http://drools.org/schema/kie-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://drools.org/schema/kie-spring http://drools.org/schema/kie-spring.xsd">

    <kie:kmodule id = "kmodule">
        <kie:kbase name="kbase" packages="rules.isString">
            <kie:ksession name="ksession"/>
        </kie:kbase>
    </kie:kmodule>

    <bean id="kiePostProcessor"
          class="org.kie.spring.annotations.KModuleAnnotationPostProcessor"/>


</beans>

3.创建规则文件

package rules.isString;
rule "测试Spring + Drools规则"
    when
    then
        System.out.println("调用规则" + drools.getRule().getName());
end;

4.创建调用文件

package com.ruleSpring;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.kie.api.cdi.KSession;
import org.kie.api.runtime.KieSession;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:Spring.xml"})
public class TestSpring {

    @KSession("ksession")
    KieSession ksession;

    @Test
    public void runRules() {
        int count = ksession.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        ksession.dispose();
    }




}

5.运行结果

2019-08-22 23:11:47 [DEBUG]org.drools.....ClasspathKieProject - KieModule URL type=file url=/D:/JavaDev/workspace/DroolsProject/DroolsSpring/target/classes
2019-08-22 23:11:47 [INFO ]org.kie..KModuleBeanFactoryPostProcessor - Adding KieModule from /D:/JavaDev/workspace/DroolsProject/DroolsSpring/target/classes to repository.
2019-08-22 23:11:47 [INFO ]org.drools.....KieRepositoryImpl - KieModule was added: FileKieModule[releaseId=com.sunlei:DroolsSpring:1.0-SNAPSHOT,file=JavaDevworkspaceDroolsProjectDroolsSpring	argetclasses]
2019-08-22 23:11:47 [DEBUG]org.drools.....KieRepositoryImpl - Cannot load a KieRepositoryScanner, using the DummyKieScanner
2019-08-22 23:11:47 [ERROR]org.drools.....KieRepositoryImpl - Cannot load artifact com.sunlei:DroolsSpring:1.0-SNAPSHOT. You need kie-ci on the classpath to perform this operation
2019-08-22 23:11:48 [WARN ]org.drools.....KieProject - No files found for KieBase kbase, searching folder JavaDevworkspaceDroolsProjectDroolsSpring	argetclasses
2019-08-22 23:11:48 [ERROR]org.drools.....KieRepositoryImpl - Cannot load artifact com.sunlei:DroolsSpring:1.0-SNAPSHOT. You need kie-ci on the classpath to perform this operation
2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was HALTING is now INACTIVE
总共执行了0条规则
2019-08-22 23:11:48 [DEBUG]org.drools...DefaultAgenda - State was INACTIVE is now DISPOSED

能够成功运行,但是运行结果是错误的。暂时还不知道为什么。。。。。。

规则then

rule是then 与end之间的部分,是规则体中的重要组成部分之一。主要用来处理相关的业务。RHS是真正做事的部分,业务操作时,基本都是通过操作Fact事实对象,并将修改过或已经得到的结果返回Java代码,并进行处理。

RHS部分是规则的结果或行动部分的通用名称,这个部分包含要执行操作的列表,RHS部分如果再次进行条件判断或执行其他语法显然是不好的。LHS部分提供了相关的判断。

一般来说,RHS部分是规则体中的最小操作。只有规则体LHS部分全部满足条件时才会执行这部分。规则体的RHS部分也应保持较小,从而去提高规则的可读性和可维护性,如果发现RHS部分中需要调用其他语言或添加条件,那么就应该将这个规则分解成多个规则。RHS部分的主要目的是插入,删除,修改工作内存数据(Fact事实对象)。

update(Fact事实对象):告诉引擎一个对象已经改变了(一个绑定在LHS部分上的引用,即$p:Person中的$p),修改成功后在工作内存中会已经发生变化。可能会导致规则再次被激活。注意只有在真正将工作内存中的值改变时,其他规则体才能正常对变化后的Fact事实对象进行判断操作。

insert(Fact事实对象):将一个新的Fact事实对象放入到工作内存中,它不仅可以操作引用的Fact事实对象,还可以操作declare声明的Fact事实对象。

insertLogical(new Object()):和insert是相似的,但是当没有更多的事实来支持当前触发规则的LHS部分时,这个Fact事实对象将会被自动删除。

delete(handle):从工作内存中删除一个Fact事实对象,和update是相似的,都是通过引用LHS部分上绑定的值,

update是去修改Fact事实对象的一种方式,Drools还提供了另外的一种方式,modify,它和update的功能是一样的。但是写法上有很大的不同。

SpringBoot + Drools(1) 

maven项目创建,pom.xml中文件如下:

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <project xmlns="http://maven.apache.org/POM/4.0.0"
  3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5     <parent>
  6         <artifactId>Code</artifactId>
  7         <groupId>com.sunlei</groupId>
  8         <version>1.0-SNAPSHOT</version>
  9     </parent>
 10     <modelVersion>4.0.0</modelVersion>
 11 
 12     <artifactId>DroolsSpringBootTest</artifactId>
 13 
 14     <properties>
 15         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 16         <drools.version>7.10.0.Final</drools.version>
 17     </properties>
 18 
 19     <dependencies>
 20         <!--start drools -->
 21         <dependency>
 22             <groupId>org.drools</groupId>
 23             <artifactId>drools-core</artifactId>
 24             <version>${drools.version}</version>
 25         </dependency>
 26 
 27         <dependency>
 28             <groupId>org.drools</groupId>
 29             <artifactId>drools-compiler</artifactId>
 30             <version>${drools.version}</version>
 31         </dependency>
 32 
 33         <dependency>
 34             <groupId>org.kie</groupId>
 35             <artifactId>kie-spring</artifactId>
 36             <version>${drools.version}</version>
 37         </dependency>
 38 
 39         <dependency>
 40             <groupId>org.kie</groupId>
 41             <artifactId>kie-internal</artifactId>
 42             <version>${drools.version}</version>
 43         </dependency>
 44 
 45         <dependency>
 46             <groupId>org.drools</groupId>
 47             <artifactId>drools-templates</artifactId>
 48             <version>${drools.version}</version>
 49         </dependency>
 50         <!--end drools -->
 51 
 52         <dependency>
 53             <groupId>junit</groupId>
 54             <artifactId>junit</artifactId>
 55             <version>4.12</version>
 56         </dependency>
 57 
 58         <!--Spring boot start -->
 59         <dependency>
 60             <groupId>org.springframework.boot</groupId>
 61             <artifactId>spring-boot-starter-web</artifactId>
 62             <!-- 如果要配置log4j2, 就要先去除logging包 -->
 63             <exclusions>
 64                 <exclusion>
 65                     <groupId>org.springframework.boot</groupId>
 66                     <artifactId>spring-boot-starter-logging</artifactId>
 67                 </exclusion>
 68             </exclusions>
 69         </dependency>
 70 
 71         <dependency>
 72             <groupId>org.springframework.boot</groupId>
 73             <artifactId>spring-boot-starter-test</artifactId>
 74             <scope>test</scope>
 75         </dependency>
 76 
 77         <dependency>
 78             <groupId>org.springframework.boot</groupId>
 79             <artifactId>spring-boot-starter-data-redis</artifactId>
 80         </dependency>
 81 
 82         <dependency>
 83             <groupId>org.springframework.boot</groupId>
 84             <artifactId>spring-boot-starter-log4j2</artifactId>
 85         </dependency>
 86 
 87         <!--Spring boot end -->
 88 
 89     </dependencies>
 90 
 91     <build>
 92         <plugins>
 93             <plugin>
 94                 <groupId>org.springframework.boot</groupId>
 95                 <artifactId>spring-boot-maven-plugin</artifactId>
 96                 <configuration>
 97                     <fork>true</fork>
 98                 </configuration>
 99             </plugin>
100         </plugins>
101     </build>
102 </project>

在resources文件夹下创建规则文件:mytest.drl文件如下:

 1 package rules
 2 import com.entity.Person
 3 rule "测试SpringBootDrools"
 4 when
 5 then
 6     System.out.println("测试SpringBootDrools");
 7 end
 8 
 9 rule "测试insertPerson值"
10 when
11     $p:Person(name=="张三的歌")
12 then
13     System.out.println("测试insertPerson值");
14 end

创建配置文件DroolsConfiguration.java

 1 package com.config;
 2 
 3 import org.kie.api.KieBase;
 4 import org.kie.api.KieServices;
 5 import org.kie.api.builder.*;
 6 import org.kie.api.runtime.KieContainer;
 7 import org.kie.api.runtime.KieSession;
 8 import org.kie.internal.io.ResourceFactory;
 9 import org.kie.spring.KModuleBeanFactoryPostProcessor;
10 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
11 import org.springframework.context.annotation.Bean;
12 import org.springframework.context.annotation.Conditional;
13 import org.springframework.context.annotation.Configuration;
14 import org.springframework.core.io.Resource;
15 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
16 import org.springframework.core.io.support.ResourcePatternResolver;
17 
18 import java.io.IOException;
19 
20 /**
21  * drools config
22  */
23 @Configuration
24 public class DroolsConfiguration {
25 
26     private static final String RULES_PATH = "rules/";
27 
28     private KieServices getKieServices() {
29         return KieServices.Factory.get();
30     }
31 
32     private Resource[] getRuleFiles() throws IOException {
33         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
34         return resourcePatternResolver.getResources(String.format("classpath*:%s**/*.*", RULES_PATH));
35     }
36 
37 
38     @Bean
39     @ConditionalOnMissingBean(KieFileSystem.class)
40     public KieFileSystem kieFileSystem() throws IOException {
41         KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
42         for (Resource file : getRuleFiles()) {
43             kieFileSystem.write(ResourceFactory.newClassPathResource(
44                     RULES_PATH + file.getFilename(), "UTF-8"));
45             return kieFileSystem;
46         }
47         return null;
48     }
49 
50     @Bean
51     @ConditionalOnMissingBean(KieContainer.class)
52     public KieContainer kieContainer() throws IOException {
53         final KieRepository kieRepository = getKieServices().getRepository();
54         kieRepository.addKieModule(
55                 new KieModule() {
56                     @Override
57                     public ReleaseId getReleaseId() {
58                         return kieRepository.getDefaultReleaseId();
59                     }
60                 }
61         );
62 
63         KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
64         kieBuilder.buildAll();
65         return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
66     }
67 
68     @Bean
69     @ConditionalOnMissingBean(KieBase.class)
70     public KieBase kieBase() throws IOException {
71         return kieContainer().getKieBase();
72     }
73 
74     @Bean
75     @ConditionalOnMissingBean(KieSession.class)
76     public KieSession kieSession() throws IOException {
77         return kieContainer().newKieSession();
78     }
79 
80     @Bean
81     @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
82     public KModuleBeanFactoryPostProcessor kiePostProcessor() {
83         return new KModuleBeanFactoryPostProcessor();
84     }
85 
86 
87 
88 
89 
90 
91 }

创建service类:

 1 package com.service;
 2 
 3 import com.entity.Person;
 4 import org.kie.api.runtime.KieSession;
 5 import org.springframework.stereotype.Service;
 6 
 7 import javax.annotation.Resource;
 8 
 9 @Service
10 public class TestService {
11 
12     @Resource
13     private KieSession kieSession;
14 
15     public int testService01() {
16         Person p = new Person();
17         p.setName("张三的歌");
18         kieSession.insert(p);
19         int ruleFiredCount = kieSession.fireAllRules();
20         return ruleFiredCount;
21     }
22 
23 }

创建Controller类:

 1 package com.controller;
 2 
 3 import com.service.TestService;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.stereotype.Controller;
 6 import org.springframework.web.bind.annotation.RequestMapping;
 7 import org.springframework.web.bind.annotation.ResponseBody;
 8 
 9 @RequestMapping("/test")
10 @Controller
11 public class TestController {
12 
13     @Autowired
14     private TestService testService;
15 
16     @ResponseBody
17     @RequestMapping("/test001")
18     public void test() {
19         int count = testService.testService01();
20         System.out.println("执行规则总数:" + count);
21     }
22 
23 }

创建启动类:

 1 package com;
 2 
 3 import org.slf4j.Logger;
 4 import org.slf4j.LoggerFactory;
 5 import org.springframework.boot.SpringApplication;
 6 import org.springframework.boot.autoconfigure.SpringBootApplication;
 7 
 8 @SpringBootApplication
 9 public class Application {
10 
11     protected static Logger logger = LoggerFactory.getLogger(Application.class);
12 
13     public static void main(String[] args) {
14         SpringApplication.run(Application.class, args);
15         logger.info("SpringBoot Start Success");
16     }
17 
18 }

启动项目,运行结果:

测试SpringBootDrools
测试insertPerson值
执行规则总数:2

SpringBoot + Drools(2) 

创建maven项目,pom.xml引入依赖

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <project xmlns="http://maven.apache.org/POM/4.0.0"
  3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5     <parent>
  6         <artifactId>Code</artifactId>
  7         <groupId>com.sunlei</groupId>
  8         <version>1.0-SNAPSHOT</version>
  9     </parent>
 10     <modelVersion>4.0.0</modelVersion>
 11 
 12     <artifactId>DroolsSpringBootTest2</artifactId>
 13 
 14     <properties>
 15         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 16         <drools.version>7.10.0.Final</drools.version>
 17         <log4j2.version>2.5</log4j2.version>
 18     </properties>
 19 
 20     <dependencies>
 21         <!--start drools -->
 22         <dependency>
 23             <groupId>org.drools</groupId>
 24             <artifactId>drools-core</artifactId>
 25             <version>${drools.version}</version>
 26         </dependency>
 27 
 28         <dependency>
 29             <groupId>org.drools</groupId>
 30             <artifactId>drools-compiler</artifactId>
 31             <version>${drools.version}</version>
 32         </dependency>
 33 
 34         <dependency>
 35             <groupId>org.kie</groupId>
 36             <artifactId>kie-spring</artifactId>
 37             <version>${drools.version}</version>
 38         </dependency>
 39 
 40         <dependency>
 41             <groupId>org.kie</groupId>
 42             <artifactId>kie-internal</artifactId>
 43             <version>${drools.version}</version>
 44         </dependency>
 45 
 46         <dependency>
 47             <groupId>org.drools</groupId>
 48             <artifactId>drools-templates</artifactId>
 49             <version>${drools.version}</version>
 50         </dependency>
 51         <!--end drools -->
 52 
 53         <!--Spring boot start -->
 54         <dependency>
 55             <groupId>org.springframework.boot</groupId>
 56             <artifactId>spring-boot-starter-web</artifactId>
 57             <exclusions>
 58                 <exclusion>
 59                     <groupId>org.springframework.boot</groupId>
 60                     <artifactId>spring-boot-starter-logging</artifactId>
 61                 </exclusion>
 62             </exclusions>
 63         </dependency>
 64 
 65         <dependency>
 66             <groupId>org.springframework.boot</groupId>
 67             <artifactId>spring-boot-starter-test</artifactId>
 68             <scope>test</scope>
 69         </dependency>
 70 
 71         <dependency>
 72             <groupId>org.springframework.boot</groupId>
 73             <artifactId>spring-boot-starter-data-redis</artifactId>
 74         </dependency>
 75 
 76         <!-- mysql -->
 77         <dependency>
 78             <groupId>mysql</groupId>
 79             <artifactId>mysql-connector-java</artifactId>
 80             <version>5.1.20</version>
 81         </dependency>
 82         <!-- mysql -->
 83 
 84         <!-- mybatis -->
 85         <dependency>
 86             <groupId>org.mybatis.spring.boot</groupId>
 87             <artifactId>mybatis-spring-boot-starter</artifactId>
 88             <version>1.1.1</version>
 89         </dependency>
 90 
 91         <dependency>
 92             <groupId>com.alibaba</groupId>
 93             <artifactId>fastjson</artifactId>
 94             <version>RELEASE</version>
 95         </dependency>
 96 
 97         <dependency>
 98             <groupId>com.google.code.json</groupId>
 99             <artifactId>gson</artifactId>
100         </dependency>
101         <!--Spring boot end -->
102 
103     </dependencies>
104 
105     <build>
106         <plugins>
107             <plugin>
108                 <groupId>org.springframework.boot</groupId>
109                 <artifactId>spring-boot-maven-plugin</artifactId>
110                 <configuration>
111                     <fork>true</fork>
112                 </configuration>
113             </plugin>
114             <plugin>
115                 <groupId>org.apache.maven.plugins</groupId>
116                 <artifactId>maven-compiler-plugin</artifactId>
117                 <configuration>
118                     <source>7</source>
119                     <target>7</target>
120                 </configuration>
121             </plugin>
122         </plugins>
123     </build>
124 
125 
126 
127 </project>

配置文件和Mapper文件:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 3         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4 
 5 <mapper namespace="com.dao.PersonDao">
 6 
 7     <sql id = "Base_Column_List">
 8         rule_name
 9     </sql>
10 
11     <select id="listAll" resultType="java.lang.String">
12         select * from person
13     </select>
14 </mapper>
server.port=8081
spring.jpa.show-sql=true
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/droolstest?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.max-active=10
spring.datasource.max-idle=5
spring.datasource.min-idle=0

创建controller

 1 package com.controller;
 2 
 3 
 4 import com.service.PersonService;
 5 import com.entity.Person;
 6 import org.springframework.stereotype.Controller;
 7 import org.springframework.web.bind.annotation.RequestBody;
 8 import org.springframework.web.bind.annotation.RequestMapping;
 9 import org.springframework.web.bind.annotation.RequestMethod;
10 import org.springframework.web.bind.annotation.ResponseBody;
11 
12 import javax.annotation.Resource;
13 import javax.servlet.http.HttpServletRequest;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 
18 
19 @RequestMapping("/person")
20 @Controller
21 public class PersonController {
22 
23     @Resource
24     private PersonService promoteService;
25 
26     @ResponseBody
27     @RequestMapping(value = "/list", method = RequestMethod.POST, produces = "application/json")
28     public Map<String, Object> testList(HttpServletRequest request, @RequestBody String requestBody) {
29         Map<String, Object> map = new HashMap<>();
30         List<Person> personList = promoteService.listPerson();
31         map.put("personList", personList);
32         return map;
33     }
34 }

创建启动类文件:

package com;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@SpringBootApplication
@MapperScan("org.dao")
public class Application {

    protected static Logger logger = LoggerFactory.getLogger(Application.class);

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DataSource();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        logger.info("SpringBoot2 Start Success");
    }

}

...

原文地址:https://www.cnblogs.com/sunl123/p/11318304.html