2.3 Drools配置文件

前面介绍了规则文件、规则文件内容,并且通过测试用例与执行结果的分析对规则引擎有了一个初步的认知。本节主要讲解Drools的配置文件,主要配置文件有两个,即pom.xml与kmodule.xml,核心内容是kmodule.xml的说明。下面先来介绍pom.xml,这里用Maven做管理项目,但这并不是唯一的。可以通过Eclipse安装插件创建项目,但要注意Drools的版本问题。如果是IDEA,就会自带Drools插件,否则.drl文件的图标也不会变。

通过pom.xml配置文件要用KIE操作统一管理规则,其中drools-compiler是必须引用的包。这个包的主要目的是对规则进行编译、构建等。

kmodule.xml是一个规则引擎的核心配置文件,它与Spring类似,都需要有相对应的配置文件做统一管理。它的主要功能是用来设置规则库名称、包路径、规则会话名称、规则会话类型等。

kmodule.xml文件要放到src/main/resources/META-INF/文件夹下,其原因通过分析如图2-10所示的源码可以得到。

030-1

图2-10 kmodule.xml配置文件路径说明

kmodule.xml配置文件,其内容为:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="rules" packages="rules.rulesHello">
        <ksession name="testhelloworld"/>
    </kbase>
</kmodule>

源码分析通过以下几点进行说明。

(1)一个kmodule.xml配置中可包含多个KieBase,每一个KieBase都有name属性,可以取任意字符串,但不能重名。

(2)KieBase有一个packages属性,它的值是一段字符串形式的路径信息,其内容是指src/main/resources目录下文件夹的名称,或者称之为包名,规则引擎会根据packages定义的内容查找规则文件。可以同时定义多个包,以逗号进行分隔。每一个KieBase可以包含多个KieSession。

(3)每一个KieSession都有一个名称,名称可以是任意字符串,但是不能重复[10]。Java代码中KieSession设置的name就是在执行规则代码时的name,是用来指定操作具体规则的。

(4)packages指定当前路径下的所有规则文件都会被读取。例如,在rules/Hello目录下定义了10个规则文件,运行时,这10个文件都会被加载,当然也只有满足条件的规则文件才会被执行。注意,如果在rules/Hello下有子目录,那么子目录中的规则文件不会被加载。

(5)避免分析(4)中的问题,解决方案有以下3种。

① 设计多个规则文件目录,每一种只放一个规则文件。虽然这种方式比较烦琐,但确实可以解决这类问题。

② 指定规则名,规则名称指规则文件中的rule参数,第7章中有指定规则名称的详细说明。

③ 指定规则文件名的方式,这种方式是通过API实现的,与第二种相似。

(6)就分析(4)而言,要注意的问题很多,同一目录下规则文件中的规则名称不能重复[11],当目录中含有多个规则文件时,通过insert进行插入对象操作,则规则文件中的所有规则都将有这样的值,规则之间既相互独立,又相互依赖。

通过上述分析,编写过程中要注意的事项还是很多的。下面分析一下执行调用规则的Java代码,删除一些无关的代码,重点关注加粗部分。

public static void main(String[] args) {
        KieServices kss = KieServices.Factory.get();
        KieContainer kc = kss.getKieClasspathContainer();
        KieSession ks = kc.newKieSession("testhelloworld");
        Person person = new Person();
        ks.insert(person);
        int count = ks.fireAllRules();
        ks.dispose();
    }

“KieServices kss = KieServices.Factory.get();”是指通过单例模式创建KieServices,既可以访问所有KIE构建和运行时的接口,也可以理解为KIE的服务。

“KieContainer kc = kss.getKieClasspathContainer();”是指创建一个KieContainer来读取路径中需要构建的文件,是KIE的容器,它提供了很多操作规则库的方法。KieServices与KieContainer均属于初始化过程,可以放在静态块或其他加载时就会执行的方法中。

在运行时,KieContainer会根据***Mode对象来创建KieModule、KieBase、KieSession对象。一般情况下,KieModule和KieBase只会创建一次,而KieSession则有可能被创建多次,因为KieSession的创建成本很低,同时KieSession包含了运行时的数据,所以可以进行销毁或创建若干次数。

“KieSession ks = kc.newKieSession("testhelloworld");”是指新建一个KIE会话,通过参数进行获取,newKieSession的参数正是在kmodule.xml中配置的名称。

“ks.insert(person);int count = ks.fireAllRules();ks.dispose();”是指实际操作规则的代码,执行规则是fireAllRules方法,返回类型是数字,表示成功匹配了多少条规则。

代码执行过程中,控制台会出现如图2-11所示的SLF4J日志异常信息。

032-1

图2-11 日志问题

修改pom.xml配置文件,添加日志操作,其内容为:

<properties>
         <drools.version>7.8.0.Final</drools.version>
         <log4j2.version>2.5</log4j2.version>
</properties>
         <!-- start Log4j2  -->
         <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j2.version}</version>
         </dependency>
         <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <!-- end Log4j2   -->

添加日志配置文件log4j2.xml到资源文件夹,其内容为:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <properties>
        <property name="pattern">%date{yyyy-mm-dd hh:mm:ss} [%-5level]
%logger{*.*...} - %msg%n%ex</property>
    </properties>
    <appenders>
        <console name="console" target="system_out">
            <patternlayout pattern="${pattern}"/>
        </console>
    </appenders>
    <loggers>
        <root level="debug">
            <appenderref ref="console"/>
        </root>
        <logger name="org.springframework" level="warn"/>
    </loggers>
</configuration>

日志文件可以根据需要进行设置,上述文件只是样板,不需要完全一样。配置完成后,再次运行Java代码,查看控制台显示信息,如图2-12所示。

033-1

图2-12 引用日志后的结果

汇报日志异常时,因为项目代码并没有使用任何与日志相关的信息,所以异常是在调用Drools过程中抛出的。根据这一猜想,通过在IDE中打断点的方式定位问题所在,当代码执行“KieServices kss = KieServices.Factory.get();”后就会出现此问题,通过对这段代码的分析,终于在KieContainerImpl类中找到“元凶”,如图2-13所示。

033-2

图2-13 源码分析


[1]XML规则文件基本已经不再使用,但不代表它不可以用。

[2]*.xls和*.xlsx是决策表。

[3]添加双引号的目的是在规则编译时将规则名转化为字符串,如果不添加,就必须定义为标准的类似定义变量名。

[4]规则库是指规则执行必要的内存数据。

[5]function是指规则引擎脚本中的函数。

[6]Rete是一种网络算法,是Drools的核心算法之一。

[7]Fact对象:Fact是指在Drools规则应用中(是规则的因子),JavaBean插入Working Memory中变成Fact之后,Fact对象不是对原来的JavaBean对象进行克隆,而是原来JavaBean对象的引用。

[8]静态方法多用于函数,是Drools自定义函数的两种方式之一。

[9]动态规则:泛指可动态变更的规则,俗称动态业务,第4章中会有详细说明。

[10]Drools 6.4版本中,KieBase名称不可以与KieSession名称相同。

[11]规则名称不能重复:指在当前规则库中相同逻辑路径下的规则名不能重复。