规则引擎-----Drools入门系列

  • Drools入门系列(一)HelloWorld
  • Drools入门系列(二)HelloWorld详解之Sample.drl
  • Drools入门系列(三)HelloWorld详解之kmodule.xml
  • Drools入门系列(四)HelloWorld详解之JUnit Test类
  • Drools入门系列(五)KIE概论
  • Drools入门系列(六)KIE之基础API详解
  • Drools入门系列(七)KIE之kmodule.xml
  • Drools入门系列(八)以编码方式完成kmodule的定义

Drools入门系列(一)HelloWorld

1、什么是Drools

Drools是用Java语言编写的开放源码的规则引擎。

那什么是规则引擎呢?参考自 百度百科 里面的定义:

规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。

Drools使用RETE算法对规则进行求值,在Drools6.0(当前最新版本)中还引进了PHREAK算法,Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。Drools 还具有其他优点:

  • 非常活跃的社区支持
  • 易用
  • 快速的执行速度
  • 在 Java 开发人员中流行
  • 与 Java Rule Engine API(JSR 94)兼容

2、一些说明

目前本人正在学习Drools过程中,准备编写一个系列文章,来记录自己的学习过程和学习心得。由于本人目前也是新手,因而文章中不可避免有着一些错误的理解,这个只有希望读者自己来判断了。

本系列文章基于当前最新版本Drools 6.0.1 Final版本。

在学习Drools的过程中,我也会编写一些学习的例子,这些例子都放在GitHub上:

https://github.com/XiongZhijun/nut-drools.git

然后我会通过一些tag来标记一些例子,读者可以自己checkout相应的tag来查看例子,一般这些tag都会有相应的注释的。

Git入门学习可以参考:Git入门资料

3、Hello World

从GitHub上下载nut-drools工程(使用上节里面的地址),checkout training_1标签,就可以看到HelloWorld的例子。工程结构如下: 
工程结构图

drools-helloworld-project 
这是一个典型的Maven工程,包含pom.xml文件,有src/main/java、src/main/resources,以及相应的测试目录。其中:

src/main/resources/META-INF :该目录中存放了一个kmodule.xml文件,该文件中声明了若干已经定义了的规则、流程文件。 
src/main/resources :该目录的子目录dtables和rules中存放了定义了规则的规则文件,本例中包含了两种定义规则的方式,一种是通过DRL(后缀.drl)文件来定义的,一种是通过Excel文件(后缀.xls)来定义的。 
src/test/java :该目录中定义了单元测试用例,就是直接测试运行规则的。 
现在可以直接执行单元测试,查看测试结果了。

4、Drools and Eclipse

Drools提供了Eclipse插件,可以在 http://www.jboss.org/drools/downloads 页面进行下载“Drools and jBPM Tools”,在这个下载包里面就包含有Eclipse插件。

装好插件后可以使用Drools透视图,然后就可以直接创建Drools Project了: 
1 
new-drools-project-1 
2 
new-drools-project-2 
3 
new-drools-project-3 
在这一步可以选择一些例子,这样在工程创建好之后就会有相应的例子程序了,上面提供的HelloWorld就是这个里面的自动创建的例子。 
4 
new-drools-project-4 
输入好GroupId、AtifactId、Version后点击Finish就可以创建好一个工程了。

大家可以注意到这个工程也是一个Maven结构的工程,除了没有pom.xml之外。不知道没有pom.xml文件这个是Drools有自己的考虑之外呢,还是这是一个bug。

PS:drools 6.5 的时候,是可以使用Eclipse插件进行创建maven工程了。
  •  

Drools入门系列(二)——HelloWorld详解之Sample.drl

我们来先看一下一个标准的规则文件定义文件是怎么样的:

package os.nut.drools

import os.nut.drools.Message;

rule "Hello World"
    when
        m : Message( status == Message.HELLO, myMessage : message )
    then
        System.out.println( myMessage );
        m.setMessage( "Goodbye cruel world" );
        m.setStatus( Message.GOODBYE );
        update( m );
end

rule "GoodBye"
    when
        Message( status == Message.GOODBYE, myMessage : message )
    then
        System.out.println( myMessage );
end

Java程序员可以很清楚看出来,这个规则文件跟一个Java文件非常类似,里面包含了很多Java语句,不用怀疑,这些就是Java代码,而不是类Java语法的代码。

上面这个规则文件里面包含4个部分:package、import和两个rule:

  • package:package语句的定义跟Java里面的package语句类似,唯一不同的就是在DRL文件中package后面跟的包名是不需要跟文件目录有对应关系的,上例就可以看出来这个不同:package定义是os.nut.drools,而所在的目录是rules。

  • import:import语句的含义跟java中是一样的,就是如果在本文件中需要使用某些类的话,需要通过import语句引入进来,如果需要的类在 package定义的包中就不需要再引入了,这个跟Java的概念是一致的。在上例中的import语句其实是没有必要的。

  • rule:上例定义了两个rule,也就是定义了两个规则。一个规则以rule关键字开始,以end关键字结束。上面一个rule包含了三个部分,分别是name、when、then。

  • name紧跟在rule关键字之后,可以是一个以引号(双引号、单引号均可)包含的字符串,可以包含空格等字符,如果字符串只包含字母、数字、下划线(也就是Java变量命名规则)的话,也可以不用引号,但是推荐使用引号。

  • when语句的意思就是执行下面then语句的条件,也就是说当when条件满足的情况下,才会执行then,类似于Java里面的if语句,为什么用when而不用if呢,这是因为when代表的意思是当什么“ 事件 ”发生时,当什么“ 事实 ”存在时,然后执行then。

  • then语句就是执行的动作,就是当什么事件发生,或者什么事实存在时,执行的动作序列。then后面的语句也就是Action。

  • 事件和事实是什么东西呢?在Drools里面这两个分别称之为“Event”和“Fact”,“事件”其实也是“事实”,只不过是一种特殊的事实而已。

  • 事实是什么东西呢?一个事实其实就是一个POJO,只不过这个Java对象是存放在一个特殊的空间里面,这个空间就是“Working Memory”,所有存放在Working Memory里面的对象都是事实(Fact)。when语句就是检查在Working Memory里面是不是存在满足条件的事实。

  • 事件呢?怎么个特殊法?这个我们可以暂时不用管它,到后面学习CEP的时候自然就会理解了。CEP是什么?现在不用管它! 
    when语句解读:

m : Message( status == Message.HELLO, myMessage : message )

上面是rule “Hello World”的when语句。这个语句是什么意思呢?它的意思就是:

当存在一个Message对象,并且这个Message的status字段值为Message.HELLO的时候,就可以执行下面的then语句了。用自然语言描述就是:当存在一个状态为HELLO的消息的事实时,就执行下面的动作,否则就不做。

其中Message()就是执行类型匹配,意思就是要求Working Memory中存在类型为Message的对象(事实),然后status==Message.HELLO语句呢,就是约束条件,表示该Message对象的status字段为HELLO才符合条件。

另外的m和myMessage分别表示什么呢?m加冒号的意思是将这个Message对象赋值给m,而myMessage加冒号表示将这个Message对象的message字段的值赋值给myMessage变量。然后在下面的then语句中使用这些定义的变量了。 
then语句解读:

System.out.println( myMessage );
m.setMessage( "Goodbye cruel world" );
m.setStatus( Message.GOODBYE );
update( m );

这个例子里面前三句都是普通的Java语句,唯一不同的就是下面这个update语句,这个语句的意思就是通知规则引擎m对象发生变化了,m是什么?m就是一个存放在Working Memory里面的一个Message事实,这句话就是说m这个事实发生了变化,那么规则引擎就需要重新进行规则运算,在本例中就是会在执行了update之后执行下面的“GoodBye”规则。

为什么执行“GoodBye”规则?GoodBye规则需要匹配的是status为GOODBYE的Message事实,但是一开始并没有这样的事实存在,只有当“Hello World”规则执行到了update语句的时候,更新了Message事实,这个时候规则引擎重新运算规则,WorkingMemory中就存在status为GOODBYE的Message事实了,“GoodBye”规则就会运行了,这个从控制台输出中就可以看出来了。


Drools入门系列(三)——HelloWorld详解之kmodule.xml

kmodule.xml文件存放在src/main/resources/META-INF/文件夹下。

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="rules" packages="rules">
        <ksession name="ksession-rules"/>
    </kbase>
    <kbase name="dtables" packages="dtables">
        <ksession name="ksession-dtables"/>
    </kbase>
</kmodule>

这个kmodule.xml的文件的定义非常简单,其实也很容易理解:

  • 一个kmodule里面包含了两个kbase,这个也是我们这个例子里面的两个用例,分别对应drl规则文件的例子,一个是对应Excel表格的规则例子。

  • 每一个kbase都有一个name,可以取任意字符串,但是不能重名。

  • 然后都有一个packages,可以看到packages里面的字符串其实就是src/main/resources下面的文件夹的名称,或者叫包名,规则引擎会根据这里定义的包来查找规则定义文件。可以同时定义多个包,以逗号分隔开来就行。

  • 每一个kbase下面可以包含多个ksession,当然本例中都自定义了一个。

  • 每一个ksession都有一个name,名字也可以是任意字符串,但是也不能重复。

  • kbase和ksession里面的name属性是全局不能重复的。

  • kbase和ksession中其实还有很多其它的属性,但是在这里不是很重要,就先不提了,后面我们会一一讲解的。

这样一个kmodule.xml文件就建立好了。

看完这个大家肯定都会有疑问:kmodule、kbase、ksession是什么东东?有什么含义吗?在接下来的章节中会一一解答的,这里大家知道这样的东西,然后依葫芦画瓢就可以了,在初学过程中这样也就够了。


Drools入门系列(四)——HelloWorld详解之JUnit Test类

测试类 
在本例中,有两个测试类,分别是DroolsTest和DecisionTableTest,分别对应DRL规则文件的测试和Excel表格规则的测试。两者的结果是一样的。基于Excel的方式我们先不管它,后面我会开辟专门的章节来讲述。 
DroolsBaseTest.java

public abstract class DroolsBaseTest {

    protected KieServices kieServices;
    protected KieContainer kieContainer;

    @Before
    public void setUp() {
        kieServices = KieServices.Factory.get();
        kieContainer = kieServices.getKieClasspathContainer();
    }

}

这是一个抽象类,就是将一些单元测试的公共的代码抽取到了本类中,在这里定义了两个对象kieServices和kieContainer,这个是我们执行规则时必备的两个对象,这两个对象的具体意义,我们后面再讨论,这里只需要知道他们是我们执行规则必备的对象就可以了。

DroolsTest.java

public class DroolsTest extends DroolsBaseTest {

    @Test
    public void test() {
        KieSession kSession = kieContainer.newKieSession("ksession-rules");
        Message message = new Message();
        message.setMessage("Hello World");
        message.setStatus(Message.HELLO);
        kSession.insert(message);
        kSession.fireAllRules();
    }
}

这个单元测试类继承自DroolsBaseTest,演示了一个规则运行的例子。

  • 利用kieContainer对象创建一个新的KieSession,创建session的时候我们传入了一个name:“ksession-rules”,这个字符串很眼熟吧,这个就是我们定义的kmodule.xml文件中定义的ksession的name。

  • KieSession就是一个到规则引擎的链接,通过它就可以跟规则引擎通讯,并且发起执行规则的操作。

  • 然后通过kSession.fireAllRules方法来通知规则引擎执行规则。

这样一个完整的Drools例子就完成了,包含了规则定义(DRL文件编写)、模块定义(kmodule.xml编写)、执行代码编写三个过程。


Drools入门系列(五)——KIE概论

1、引言

在上一章节我们用到了几个类和他们的对象:KieServices、KieContainer、KieSession,新入门的人肯定很困惑,这几个类都是干啥的,都有什么作用啊?然后再kmodule.xml配置文件里面配置了kbase、ksession,这些东西都是什么玩意?本章以及后面可能的几章就是要解决这些问题。

2、什么是KIE?

KIE是jBoss里面一些相关项目的统称,下图就是KIE代表的一些项目,其中我们比较熟悉的就有jBPM和Drools。

这些项目都有一定的关联关系,并且存在一些通用的API,比如说涉及到构建(building)、部署(deploying)和加载(loading)等方面的,这些API就都会以KIE作为前缀来表示这些是通用的API。前面看到的一些KieServices、KieContainer、KieSession类就都是KIE的公共API。

总的来说,就是jBoss通过KIE将jBPM和Drools等相关项目进行了一个整合,统一了他们的使用方式。像KieServices这些KIE类就是整合后的结果,在Drools中这样使用,在jBPM里面也是这样使用。 
KIE内部结构图

3、KIE项目生命周期

一个Drools应用项目其实就是一个KIE项目,KIE的生命周期其实就是Drools和jBPM这些项目的生命周期。

KIE项目生命周期包含:编写(Author)、构建(Build)、测试(Test)、部署(Deploy)、使用(Utilize)、执行(Run)、交互(Work)、管理(Manage)。

编写:编写就是编写规则文件或者流程文件; 
构建:就是构建一个可以发布部署的组件,在KIE中就是构建一个jar文件; 
测试:在部署到应用程序之前需要对规则或者流程进行测试; 
部署:就是将jar部署到应用程序,KIE利用Maven仓库来进行发布和部署; 
使用:就是加载jar文件,并通过KieContainer对jar文件进行解析,然后创建KieSession; 
执行:系统通过KieSession对象的API跟Drools引擎进行交互,执行规则或者流程; 
交互:用户通过命令行或者UI跟引擎进行交互; 
管理:管理KieSession或者KieContainer对象。

4、KIE & Maven

通过前面的知识我们了解到Drools工程其实就是一个Maven工程,有着Maven工程标准的结构,然后Drools在这个基础上也定义了一个自己的存储结构: 
Maven项目结构 
drools的标准存储结构就是在src/main/resources文件夹下面存储规则文件(包括DRL文件和Excel文件),然后在META-INF文件夹下面创建一个kmodule.xml文件用来存储规则定义声明。

Drools项目最终都是打包成jar然后进行发布部署的(KIE项目生命周期提到的),这样定义工程结构和打包发布方式的根本原因就是——Maven! 
maven 
上图描述了KIE项目(包括Drools)的打包、发布、部署过程,就是一个KIE项目按照上面定义的工程结构进行设计开发,然后通过mvn deploy命令发布到Maven仓库,然后应用程序可以通过mvn install将发布好的jar包下载安装到本地应用程序中,最后通过KieServices等API就可以直接使用这些发布好的规则了。

为什么我们写的JUnit Test类里面驱动一个规则的代码非常简单,就是因为Drools定义了上面的一套规范,按照规范来编写、发布、部署规则之后就可以确保以最简单的方式来使用Drools等KIE项目。这也是惯例优于配置的一种体现。

所以我们说一个Drools项目工程就是一个Maven项目工程,或者说一个KIE项目工程就是一个Maven工程。

KIE也提供了一种策略,能够让应用程序在运行时,能够动态监测Maven仓库中Drools项目jar组件的版本更新情况,然后可以根据配置动态更新Drools发布包,实现热插拔功能,这个是通过KieScanner API实现的。


Drools入门系列(六)——KIE之基础API详解

在有些术语使用的时候,我有时候会用KIE项目、KIE引擎或者Drools项目、Drools引擎,大家应该理解KIE是Drools等项目的一个统称,所以在大多数情况下KIE或者特指Drools都是差不多的。

现在我们开始了解KIE的相关API,在这个helloworld例子中,我们接触过如下这些类和接口: 
基本类和接口 
我们通过KieServices对象得到一个KieContainer,然后KieContainer根据session name来新建一个KieSession,最后通过KieSession来运行规则。

KieServices:

该接口提供了很多方法,可以通过这些方法访问KIE关于构建和运行的相关对象,比如说可以获取KieContainer,利用KieContainer来访问KBase和KSession等信息;可以获取KieRepository对象,利用KieRepository来管理KieModule等。

KieServices就是一个中心,通过它来获取的各种对象来完成规则构建、管理和执行等操作。

KieContainer:

可以理解KieContainer就是一个KieBase的容器,KieBase是什么呢?

KieBase:

KieBase就是一个知识仓库,包含了若干的规则、流程、方法等,在Drools中主要就是规则和方法,KieBase本身并不包含运行时的数据之类的,如果需要执行规则KieBase中的规则的话,就需要根据KieBase创建KieSession。 
KieSession:

KieSession就是一个跟Drools引擎打交道的会话,其基于KieBase创建,它会包含运行时数据,包含“事实 Fact”,并对运行时数据事实进行规则运算。我们通过KieContainer创建KieSession是一种较为方便的做法,其实他本质上是从KieBase中创建出来。的。

KieSession就是应用程序跟规则引擎进行交互的会话通道。

创建KieBase是一个成本非常高的事情,KieBase会建立知识(规则、流程)仓库,而创建KieSession则是一个成本非常低的事情,所以KieBase会建立缓存,而KieSession则不必。

较为完善的类关系如下: 
详细关系 
KieRepository:

KieRepository是一个单例对象,它是一个存放KieModule的仓库,KieModule由kmodule.xml文件定义(当然不仅仅只是用它来定义)。

KieProject:

KieContainer通过KieProject来初始化、构造KieModule,并将KieModule存放到KieRepository中,然后KieContainer可以通过KieProject来查找KieModule定义的信息,并根据这些信息构造KieBase和KieSession。

ClasspathKieProject:

ClasspathKieProject实现了KieProject接口,它提供了根据类路径中的META-INF/kmodule.xml文件构造KieModule的能力,也就是我们能够基于Maven构造Drools组件的基本保障之一。

意味着只要我们按照前面提到过的Maven工程结构组织我们的规则文件或流程文件,我们就能够只用很少的代码完成模型的加载和构建。 
现在我们知道了可以通过ClasspathKieProject来解析kmodule.xml文件来构建KieModule,那么整个过程是如何进行的呢?kmodule.xml里面的kbase、ksession和KieBase和KieSession又是什么关系呢?下一章节我们继续。


Drools入门系列(七)——KIE之kmodule.xml

一个标准的kmodule.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="rules" packages="rules">
        <ksession name="ksession-rules"/>
    </kbase>
    <kbase name="dtables" packages="dtables">
        <ksession name="ksession-dtables"/>
    </kbase>
</kmodule>

上一章节我们提到了ClasspathKieProject根据kmodule.xml文件来构造KieModule对象。KieModule是什么呢?跟KieBase、KieSession之间又是什么关系呢?我们看一下: 
KieBase KieModule KieSession 
上图可以以关系图的方式来理解,而不是准确表示类之间的关系。

从总的来说,左边的接口描述了一种“定义”,而右边的接口则描述的是一种运行时对象,右边的运行时对象是根据左边的定义创建出来的。

然后我们看到很多的“包含”关系,左边的关系体现的就是在kmodule.xml文件中的kmodule、kbase和ksession的定义和从上到下的包含关系。

在ClasspathKieProject类中,会根据kmodule.xml文件的定义,将其解析并生成成KieModuleModel、KieBaseModel、KieSessionModel对象,基于这个原理,那么我们也可以抛开kmodule.xml文件,通过编程的方式创建这些Model对象,这个在稍后的章节中会讲到。

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

kmodule.xml文件中的kbase和ksession标签都有很多的属性,这些属性映射到Java对象的时候就对应着相关的对象的字段,下面我们详细了解一下有那些属性: 
ps:懒得打字了,汗,大家别嫌弃 
kbase的属性: 
kbase的属性
ksession的属性: 
ksession的属性
这样我们就可以通过kmodule.xml文件来定义KieModule了,ClasspathKieProject会自动解析classpath下面的所有META-INF/kmodule.xml文件,然后解析成KieModule对象供Drools引擎使用。


Drools入门系列(八)——以编码方式完成kmodule的定义

在Git里面checkout training_2这个例子,就会发现在多了一个KieFileSystemTest单元测试:

public class KieFileSystemTest {

    @Test
    public void test() {
        KieServices kieServices = KieServices.Factory.get();
        KieResources resources = kieServices.getResources();
        KieModuleModel kieModuleModel = kieServices.newKieModuleModel();//1

        KieBaseModel baseModel = kieModuleModel.newKieBaseModel(
                "FileSystemKBase").addPackage("rules");//2
        baseModel.newKieSessionModel("FileSystemKSession");//3
        KieFileSystem fileSystem = kieServices.newKieFileSystem();

        String xml = kieModuleModel.toXML();
        System.out.println(xml);//4
        fileSystem.writeKModuleXML(xml);//5

        fileSystem.write("src/main/resources/rules/rule.drl", resources
                .newClassPathResource("kiefilesystem/KieFileSystemTest.drl"));//6

        KieBuilder kb = kieServices.newKieBuilder(fileSystem);
        kb.buildAll();//7
        if (kb.getResults().hasMessages(Level.ERROR)) {
            throw new RuntimeException("Build Errors:\n"
                    + kb.getResults().toString());
        }
        KieContainer kContainer = kieServices.newKieContainer(kieServices
                .getRepository().getDefaultReleaseId());

        assertNotNull(kContainer.getKieBase("FileSystemKBase"));
        KieSession kSession = kContainer.newKieSession("FileSystemKSession");

        kSession.fireAllRules();
    }
}

这个用例演示了如何利用编码的方式来构建kmodule了,整个流程很简单,就是:

  1. 先创建KieModuleModel;
  2. 再创建KieBaseModel;
  3. 然后创建 KieSessionModel;
  4. 创建完成之后可以生产一个xml文件,就是kmodule.xml文件了;
  5. 将这个xml文件写入到KieFileSystem中;
  6. 然后将规则文件等写入到KieFileSystem中;
  7. 最后通过KieBuilder进行构建就将该kmodule加入到KieRepository中了。这样就将自定义的kmodule加入到引擎中了,就可以按照之前的方法进行使用了。

参考资料

[1] http://www.tuicool.com/articles/3EFNV3M 
[2] http://www.tuicool.com/articles/JV7J7zr 
[3] http://www.tuicool.com/articles/ememuq 
[4] http://www.tuicool.com/articles/InMjei 
[5] http://www.tuicool.com/articles/b2yqeq 
[6] http://www.tuicool.com/articles/jeIVjiy 
[7]http://www.tuicool.com/articles/22au6zV 
[8] http://www.tuicool.com/articles/qqIFvy

猜你喜欢

转载自blog.csdn.net/weberhuangxingbo/article/details/83823550