Drools规则引擎开发教程

hld_hepeng 贡献于2011-07-29

作者 万平  创建于2010-10-15 05:16:00   修改者User  修改于2010-11-26 06:43:00字数61912

文档摘要:Drools是一款基于Java的开源规则引擎,所以在使用Drools之前需要在开发机器上安装好JDK环境,Drools5要求的JDK版本要在1.5或以上。
关键词:

 1. 学习前的准备 Drools是一款基于Java的开源规则引擎,所以在使用Drools之前需要在开发机器上安装好JDK环境,Drools5要求的JDK版本要在1.5或以上。 1.1. 开发环境搭建 大多数软件学习的第一步就是搭建这个软件的开发环境,Drools也不例外。本小节的内容就是介绍如何搭建一个Drools5的开发、运行、调试环境。 1.1.1. 下载开发工具 Drools5提供了一个基于Eclipse3.4的一个IDE开发工具,所以在使用之前需要到http://eclipse.org网站下载一个3.4.x版本的Eclipse,下载完成之后,再到http://jboss.org/drools/downloads.html 网站,下载Drools5的Eclipse插件版IDE及Drools5的开发工具包,如图1-1所示。 除这两个下载包以外,还可以把Drools5的相关文档、源码和示例的包下载下来参考学 习使用。 将下载的开发工具包及IDE包解压到一个非中文目录下,解压完成后就可以在 Eclipse3.4上安装Drools5提供的开发工具IDE了。 1.1.2. 安装Drools IDE 打开Eclipse3.4所在目录下的links目录(如果该目录不存在可以手工在其目录下创建 一个links目录),在links目录下创建一个文本文件,并改名为drools5-ide.link,用记事本打 开该文件,按照下面的版本输入Drools5 Eclipse Plugin文件所在目录: path=D:\\eclipse\\drools-5.0-eclipse-all 这个值表示Drools5 Eclipse Plugin文件位于D盘eclipse目录下的drools-5.0-eclipse-all 下面,这里有一点需要注意,那就是drools-5.0-eclipse-all文件夹下必须再包含一个eclipse 目录,所有的插件文件都应该位于该eclipse目录之下,接下来要在win dos下重启Eclipse 3.4, 检验Drools5 IDE是否安装成功。 进入win dos,进入Eclipes3.4所在目录,输入eclipse –clean启动Eclipse3.4。启动完成后打开菜单Window寐Preferences,在弹出的窗口当中如果能在左边导航树中发现Drools节点就表示Drools5 IDE安装成功了,如图1-2所示。 IDE安装完成后,接下来需要对Drools5的IDE环境进行简单的配置,打开菜单 Window寐Preferences,在弹出的窗口当中选择左边导航树菜单Drools寐Installed Drools Runtimes设置Drools5 IDE运行时依赖的开发工具包,点击“Add…”按钮添加一个开发工 具包,如图1-3所示。 图1-3当中添加了一个开发工具包,名称为“drools-5.0.0”,对应的工具包目录为D盘 doc\about rule\drools5.x\drools-5.0-bin目录。添加完成后这样Drools5的开发环境就搭好了。 下面我们就来利用这个环境编写一个规则看看运行效果。 1.2. 编写第一个规则 1.3. 规则的编译与运行 在Drools当中,规则的编译与运行要通过Drools提供的各种API来实现,这些API总 体来讲可以分为三类:规则编译、规则收集和规则的执行。完成这些工作的API主要有 KnowledgeBuilder、KnowledgeBase、StatefulKnowledgeSession、StatelessKnowledgeSession、、 等,它们起到了对规则文件进行收集、编译、查错、插入fact、设置global、执行规则或规 则流等作用,在正式接触各种类型的规则文件编写方式及语法讲解之前,我们有必要先熟悉 一下这些API的基本含义及使用方法。 1.3.1. KnowledgeBuilder 规则编写完成之后,接下来的工作就是在应用的代码当中调用这些规则,利用这些编写 好的规则帮助我们处理业务问题。KnowledgeBuilder的作用就是用来在业务代码当中收集已 经编写好的规则,然后对这些规则文件进行编译,最终产生一批编译好的规则包 (KnowledgePackage)给其它的应用程序使用。KnowledgeBuilder在编译规则的时候可以通 过其提供的hasErrors()方法得到编译规则过程中发现规则是否有错误,如果有的话通过其提 供的getErrors()方法将错误打印出来,以帮助我们找到规则当中的错误信息。 创建KnowledgeBuilder对象使用的是KnowledgeBuilderFactory的newKnowledgeBuilder 方法。代码清单1-1就演示了KnowledgeBuilder的用法。 代码清单1-1: import java.util.Collection; import java.util.Iterator; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderErrors; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; public class Test { public static void main(String[] args) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("test.drl",Tes.class), ResourceType.DRL); if (kbuilder.hasErrors()) { System.out.println("规则中存在错误,错误消息如下:"); KnowledgeBuilderErrors kbuidlerErrors = kbuilder.getErrors(); for (Iterator iter = kbuidlerErrors.iterator(); iter.hasNext();) { System.out.println(iter.next()); } } Collection kpackage = kbuilder.getKnowledgePackages();// 产生规则包的集合 } } 通过KnowledgeBuilder编译的规则文件的类型可以有很多种,如.drl文件、.dslr文件或 一个xls文件等。产生的规则包可以是具体的规则文件形成的,也可以是规则流(rule flow) 文件形成的,在添加规则文件时,需要通过使用ResourceType的枚举值来指定规则文件的 类型;同时在指定规则文件的时候drools还提供了一个名为ResourceFactory的对象,通过 该对象可以实现从Classpath、URL、File、ByteArray、Reader或诸如XLS的二进制文件里 添加载规则。 在规则文件添加完成后,可以通过使用hasErrors()方法来检测已添加进去的规则当中有 没有错误,如果不通过该方法检测错误,那么如果规则当中存在错误,最终在使用的时候也 会将错误抛出。代码清单1-2就演示了通过KnowledgeBuilder来检测规则当中有没有错误。 代码清单 1-2: import java.util.Collection; import java.util.Iterator; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderErrors; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; public class Test { public static void main(String[] args) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL); if (kbuilder.hasErrors()) { System.out.println("规则中存在错误,错误消息如下:"); KnowledgeBuilderErrors kbuidlerErrors = kbuilder.getErrors(); for (Iterator iter = kbuidlerErrors.iterator(); iter.hasNext();) { System.out.println(iter.next()); } } Collection kpackage = kbuilder.getKnowledgePackages();// 产生规则包的集合 } } 后面随着介绍的深入我们还会看到KnowledgeBuilder的一些其它用法。 1.3.2. KnowledgeBase KnowledgeBase是Drools提供的用来收集应用当中知识(knowledge)定义的知识库对 象,在一个KnowledgeBase当中可以包含普通的规则(rule)、规则流(rule flow)、函数定义 (function)、用户自定义对象(type model)等。KnowledgeBase本身不包含任何业务数据对 象(fact对象,后面有相应章节着重介绍fact对象),业务对象都是插入到由KnowledgeBase 产生的两种类型的session对象当中(StatefulKnowledgeSession和StatelessKnowledgeSession, 后面会有对应的章节对这两种类型的对象进行介绍),通过session对象可以触发规则执行或 开始一个规则流执行。 创建一个KnowledgeBase要通过KnowledgeBaseFactory对象提供的newKnowledgeBase ()方法来实现,这其中创建的时候还可以为其指定一个KnowledgeBaseConfiguration对象, KnowledgeBaseConfiguration对象是一个用来存放规则引擎运行时相关环境参数定义的配置 对象,代码清单1-3演示了一个简单的KnowledgeBase对象的创建过程。 代码清单 1-3: import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; public class Test { public static void main(String[] args) { KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); } } 代码清单1-4演示了创建KnowledgeBase过程当中,使用一KnowledgeBaseConfiguration对象来设置环境参数。 代码清单 1-4: import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; public class Test { public static void main(String[] args) { KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory .newKnowledgeBaseConfiguration(); kbConf.setProperty("org.drools.sequential", "true"); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbConf); } } 从代码清单1-4中可以看到,创建一个KnowledgeBaseConfiguration对象的方法也是使 用KnowldegeBaseFactory,使用的是其提供的newKnowledgeBaseConfiguration()方法,该方 法创建好的KnowledgeBaseConfiguration对象默认情况下会加载drools-core-5.0.1.jar包下 META-INF/drools.default.rulebase.conf文件里的规则运行环境配置信息,加载完成后,我们 可以在代码中对这些默认的信息重新赋值,以覆盖加载的默认值,比如这里我们就把 org.drools.sequential的值修改为true,它的默认值为false。 除了这种方式创建KnowledgeBaseConfiguration方法之外,我们还可以为其显示的指定 一个Properties对象,在该对象中设置好需要覆盖默认值的相关属性的值,然后再通过 newKnowledgeBaseConfiguration(Properties prop,ClassLoader loader)方法创建一个 KnowledgeBaseConfiguration对象。该方法方法当中第一个参数就是我们要设置的Properties 对象,第二个参数用来设置加载META-INF/drools.default.rulebase.conf文件的ClassLoader, 因为该文件在ClassPath下,所以采用的是ClassLoader方法进行加载,如果不指定这个参数, 那么就取默认的ClassLoader对象,如果两个参数都为null,那么就和 newKnowledgeBaseConfiguration()方法的作用相同了,代码清单代码清单1-5演示了这种用 法。 代码清单 1-5: import java.util.Properties; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; public class Test { public static void main(String[] args) { Properties properties = new Properties(); properties.setProperty("org.drools.sequential", "true"); KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory .newKnowledgeBaseConfiguration(properties, null); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbConf); } } 用来设置默认规则运行环境文件drools.default.rulebase.conf里面所涉及到的具体项内容如代码清单1-6所示: 代码清单 1-6: drools.maintainTms = drools.assertBehaviour = drools.logicalOverride = drools.sequential = drools.sequential.agenda = drools.removeIdentities = drools.shareAlphaNodes = drools.shareBetaNodes = drools.alphaNodeHashingThreshold = drools.compositeKeyDepth = drools.indexLeftBetaMemory = drools.indexRightBetaMemory = drools.consequenceExceptionHandler = drools.maxThreads = drools.multithreadEvaluation = 在后面的内容讲解过程当中,对于代码清单1-6里列出的属性会逐个涉及到,这里就不 再多讲了。 KnowledgeBase创建完成之后,接下来就可以将我们前面使用KnowledgeBuilder生成的 KnowledgePackage的集合添加到KnowledgeBase当中,以备使用,如代码清单1-7所示。 代码清单 1-7: import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.definition.KnowledgePackage; import org.drools.io.ResourceFactory; public class Test { public static void main(String[] args) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL); Collection kpackage = kbuilder.getKnowledgePackages(); KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory .newKnowledgeBaseConfiguration(); kbConf.setProperty("org.drools.sequential", "true"); KnowledgeBase kbase = KnowledgeBaseFactory. newKnowledgeBase(kbConf); kbase.addKnowledgePackages(kpackage);// 将KnowledgePackage集合 添加到KnowledgeBase当中 } } 1.3.3. StatefulKnowledgeSession 规则编译完成之后,接下来就需要使用一个API使编译好的规则包文件在规则引擎当 中运行起来。在Drools5当中提供了两个对象与规则引擎进行交互:StatefulKnowledgeSession 和StatelessKnowledgeSession,本小节当中要介绍的是StatefulKnowledgeSession对象,下面 的一节将对StatelessKnowledgeSession对象进行讨论。 StatefulKnowledgeSession对象是一种最常用的与规则引擎进行交互的方式,它可以与规 则引擎建立一个持续的交互通道,在推理计算的过程当中可能会多次触发同一数据集。在用 户的代码当中,最后使用完StatefulKnowledgeSession对象之后,一定要调用其dispose()方 法以释放相关内存资源。 StatefulKnowledgeSession可以接受外部插入(insert)的业务数据——也叫fact,一个 fact对象通常是一个普通的Java的POJO,一般它们会有若干个属性,每一个属性都会对应 getter和setter方法,用来对外提供数据的设置与访问。一般来说,在Drools规则引擎当中, fact所承担的作用就是将规则当中要用到的业务数据从应用当中传入进来,对于规则当中产 生的数据及状态的变化通常不用fact传出。如果在规则当中需要有数据传出,那么可以通过 在StatefulKnowledgeSession当中设置global对象来实现,一个global对象也是一个普通的 Java对象,在向StatefulKnowledgeSession当中设置global对象时不用insert方法而用 setGlobal方法实现。 创建一个StatefulKnowledgeSession要通过KnowledgeBase对象来实现,下面的代码清 单1-8就演示了StatefulKnowledgeSession的用法: 代码清单 1-8: import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.definition.KnowledgePackage; import org.drools.io.ResourceFactory; import org.drools.runtime.StatefulKnowledgeSession; public class Test { public static void main(String[] args) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL); Collection kpackage = kbuilder.getKnowledgePackages(); KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory .newKnowledgeBaseConfiguration(); kbConf.setProperty("org.drools.sequential", "true"); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbConf); kbase.addKnowledgePackages(kpackage);// 将KnowledgePackage集合添加到KnowledgeBase当中 StatefulKnowledgeSession statefulKSession = kbase .newStatefulKnowledgeSession(); statefulKSession.setGlobal("globalTest", new Object());// 设置一个global对象 statefulKSession.insert(new Object());// 插入一个fact对象 statefulKSession.fireAllRules(); statefulKSession.dispose(); } } 代码清单1-8当中同时也演示了规则完整的运行处理过程,可以看到,它的过程是首先 需要通过使用KnowledgeBuilder将相关的规则文件进行编译,产生对应的KnowledgePackage 集合,接下来再通过KnowledgeBase把产生的KnowledgePackage集合收集起来,最后再产 生StatefulKnowledgeSession将规则当中需要使用的fact对象插入进去、将规则当中需要用 到的global设置进去,然后调用fireAllRules()方法触发所有的规则执行,最后调用dispose() 方法将内存资源释放。 1.3.4. StatelessKnowledgeSession StatelessKnowledgeSession的作用与StatefulKnowledgeSession相仿,它们都是用来接收 业务数据、执行规则的。事实上,StatelessKnowledgeSession对StatefulKnowledgeSession做 了包装,使得在使用StatelessKnowledgeSession对象时不需要再调用dispose()方法释放内存 资源了。 因为StatelessKnowledgeSession本身所具有的一些特性,决定了它的使用有一定的局限 性。在使用StatelessKnowledgeSession时不能进行重复插入fact的操作、也不能重复的调用 fireAllRules()方法来执行所有的规则,对应这些要完成的工作在StatelessKnowledgeSession 当中只有execute(…)方法,通过这个方法可以实现插入所有的fact并且可以同时执行所有的 规则或规则流,事实上也就是在执行execute(…)方法的时候就在StatelessKnowledgeSession 内部执行了insert()方法、fireAllRules()方法和dispose()方法。 代码清单1-9演示了StatelessKnowledgeSession对象的用法。 代码清单1-9: import java.util.ArrayList; import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.definition.KnowledgePackage; import org.drools.io.ResourceFactory; import org.drools.runtime.StatelessKnowledgeSession; public class Test { public static void main(String[] args) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL); Collection kpackage = kbuilder.getKnowledgePackages(); KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory .newKnowledgeBaseConfiguration(); kbConf.setProperty("org.drools.sequential", "true"); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbConf); kbase.addKnowledgePackages(kpackage);// 将KnowledgePackage集合添加到KnowledgeBase当中 StatelessKnowledgeSession statelessKSession = kbase .newStatelessKnowledgeSession(); ArrayList list = new ArrayList(); list.add(new Object()); list.add(new Object()); statelessKSession.execute(list); } } 代码清单1-9当中,通过新建了一个ArrayList对象,将需要插入到StatelessKnowledgeSession当中的对象放到这个ArrayList当中,将这个ArrayList作为参数传给execute(…)方法,这样在StatelessKnowledgeSession内部会对这个ArrayList进行迭代,取出其中的每一个 Element,将其作为fact,调用StatelessKnowledgeSession对象内部的StatefulKnowledgeSession对象的insert()方法将产生的fact逐个插入到StatefulKnowledgeSession当中,然后调用StatefulKnowledgeSession的fireAllRules()方法,最后执行dispose()方法释放内存资源。 在代码清单1-9当中,如果我们要插入的fact就是这个ArrayList而不是它内部的Element 那该怎么做呢?在StatelessKnowledgeSession当中,还提供了execute(Command cmd)的方法, 在该方法中通过CommandFactory可以创建各种类型的Command,比如前面的需求要直接 将这个ArrayList作为一个fact插入,那么就可以采用CommandFactory.newInsert(Object obj) 来实现,代码清单1-9当中execute方法可做如代码清单1-10所示的修改。 代码清单 1-10: statelessKSession.execute(CommandFactory.newInsert(list)); 如果需要通过StatelessKnowledgeSession设置global的话,可以使用 CommandFactory.newSetGlobal(“key”Object obj)来实现;如果即要插入若干个fact,又要设 置相关的global,那么可以将CommandFactory产生的Command对象放在一个Collection当 中,然后再通过CommandFactory.newBatchExecution(Collection collection)方法实现。代码清 单1-11演示了这种做法。 代码清单 1-11 import java.util.ArrayList; import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.command.Command; import org.drools.command.CommandFactory; import org.drools.definition.KnowledgePackage; import org.drools.io.ResourceFactory; import org.drools.runtime.StatelessKnowledgeSession; public class Test { public static void main(String[] args) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL); Collection kpackage = kbuilder.getKnowledgePackages(); KnowledgeBaseConfiguration kbConf = KnowledgeBaseFactory .newKnowledgeBaseConfiguration(); kbConf.setProperty("org.drools.sequential", "true"); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbConf); kbase.addKnowledgePackages(kpackage);// 将KnowledgePackage集合添加到KnowledgeBase当中 StatelessKnowledgeSession statelessKSession = kbase .newStatelessKnowledgeSession(); ArrayList list = new ArrayList(); list.add(CommandFactory.newInsert(new Object())); list.add(CommandFactory.newInsert(new Object())); list.add(CommandFactory.newSetGlobal("key1", new Object())); list.add(CommandFactory.newSetGlobal("key2", new Object())); statelessKSession.execute(CommandFactory.newBatchExecution(list)); } } 1.4. Fact对象 Fact是指在Drools规则应用当中,将一个普通的JavaBean插入到规则的WorkingMemory 当中后的对象。规则可以对Fact对象进行任意的读写操作,当一个JavaBean插入到 WorkingMemory当中变成Fact之后,Fact对象不是对原来的JavaBean对象进行Clon,而是 原来JavaBean对象的引用。规则在进行计算的时候需要用到应用系统当中的数据,这些数 据设置在Fact对象当中,然后将其插入到规则的WorkingMemory当中,这样在规则当中就 可以通过对Fact对象数据的读写,从而实现对应用数据的读写操作。一个Fact对象通常是 一个具有getter和setter方法的POJO对象,通过这些getter和setter方法可以方便的实现对 Fact对象的读写操作,所以我们可以简单的把Fact对象理解为规则与应用系统数据交互的桥梁或通道。 当Fact对象插入到WorkingMemory当中后,会与当前WorkingMemory当中所有的规 则进行匹配,同时返回一个FactHandler对象。FactHandler对象是插入到WorkingMemory 当中Fact对象的引用句柄,通过FactHandler对象可以实现对对应的Fact对象的删除及修改 等操作。 在前面介绍StatefulKnowledgeSession和StatelessKnowledgeSession两个对象的时候也提 到了插入Fact对象的方法,在StatefulKnowledgeSession当中直接使用insert方法就可以将 一个Java对象插入到WokingMemory当中,如果有多个Fact需要插入,那么多个调用insert 方法即可;对于StatelessKnowledgeSession对象可利用CommandFactory实现单个Fact对象 或多个Fact对象的插入。 2. 规则 学习Drools规则语法的目的是为了在应用当中帮助我们解决实际的问题,所以学会并 灵活的在规则当中使用就显的尤为重要。本章的内容包括规则的基本的约束部分语法讲解 (LHS)、规则动作执行部分语法讲解及规则的各种属性介绍。 2.1.规则文件 在Drools当中,一个标准的规则文件就是一个以“.drl”结尾的文本文件,由于它是一 个标准的文本文件,所以可以通过一些记事本工具对其进行打开、查看和编辑。规则是放在 规则文件当中的,一个规则文件可以存放多个规则,除此之外,在规则文件当中还可以存放 用户自定义的函数、数据对象及自定义查询等相关在规则当中可能会用到的一些对象。 一个标准的规则文件的结构如代码清单2-1所示。 代码清单 2-1: Package imports globals functions queries rules 对于一个规则文件而言,首先声明package是必须的,除package之外,其它对象在规 则文件中的顺序是任意的,也就是说在规则文件当中必须要有一个package声明,同时 package声明必须要放在规则文件的第一行。 规则文件当中的package和Java语言当中的package有相似之处,也有不同之处。在Java 当中package的作用是用来对功能相似或相关的文件放在同一个package下进行管理,这种 package管理既有物理上Java文件位置的管理也有逻辑上的文件位置的管理,在Java当中 这种通过package管理文件要求在文件位置在逻辑上与物理上要保持一致;在Drools的规则 文件当中package对于规则文件中规则的管理只限于逻辑上的管理,而不管其在物理上的位 置如何,这点是规则与Java文件的package的区别。 对于同一package下的用户自定义函数、自定义的查询等,不管这些函数与查询是否在 同一个规则文件里面,在规则里面是可以直接使用的,这点和Java的同一package里的Java 类调用是一样的。 2.2.规则语言 规则是在规则文件当中编写,所以要编写一个规则首先需要先创建一个存放规则的规则 文件。一个规则文件可以存放若干个规则,每一个规则通过规则名称来进行标识。代码清单 2-2说明了一个标准规则的结构。 代码清单 2-2 rule "name" attributes when LHS then RHS end 从代码清单2-2中可以看到,一个规则通常包括三个部分:属性部分(attribute)、条件 部分(LHS)和结果部分(RHS)。对于一个完整的规则来说,这三个部分都是可选的,也 就是说如代码清单2-3所示的规则是合法的。 代码清单 2-3 rule "name" when then end 对于代码清单2-3所示的这种规则,因为其没有条件部分,默认它的条件部分是满足的, 因为其结果执行部分为空,所以即使条件部分满足该规则也什么都不做。接下来我们就分别 来对规则的条件部分、结果部分和属性部分进行介绍。 2.2.1. 条件部分 条件部分又被称之为Left Hand Side,简称为LHS,下文当中,如果没有特别指出,那 么所说的LHS均指规则的条件部分,在一个规则当中when与then中间的部分就是LHS部 分。在LHS当中,可以包含0~n个条件,如果LHS部分没空的话,那么引擎会自动添加一 个eval(true)的条件,由于该条件总是返回true,所以LHS为空的规则总是返回true。所以 代码清单2-3所示的规则在执行的时候引擎会修改成如代码清单2-4所示的内容。 代码清单 2-4 rule "name" when eval(true) then end LHS部分是由一个或多个条件组成,条件又称之为pattern(匹配模式),多个pattern 之间用可以使用and或or来进行连接,同时还可以使用小括号来确定pattern的优先级。 一个pattern的语法如代码清单2-5所示: 代码清单 2-5 [绑定变量名:]Object([field约束]) 对于一个pattern来说“绑定变量名”是可选的,如果在当前规则的LHS部分的其它的 pattern要用到这个对象,那么可以通过为该对象设定一个绑定变量名来实现对其引用,对于绑定变量的命名,通常的作法是为其添加一个“$”符号作为前缀,这样可以很好的与Fact 的属性区别开来;绑定变量不仅可以用在对象上,也可以用在对象的属性上面,命名方法与 对象的命名方法相同;“field约束”是指当前对象里相关字段的条件限制,代码清单2-6规 则中LHS部分单个pattern的情形。 代码清单 2-6: rule "name" when $customer:Customer(); then end 代码清单2-6规则中“$customer”是就是一个绑定到Customer对象的“绑定变量名”, 该规则的LHS部分表示,要求Fact对象必须是Customer类型,该条件满足了那么它的LHS 会返回true。代码清单2-7演示了多个约束构成的LHS部分。 代码清单 2-7: rule "name" when $customer:Customer(age>20,gender==’male’); Order(customer==$customer,price>1000); then end 代码清单2-7中的规则就包含两个pattern,第一个pattern有三个约束,分别是:对象 类型必须是Cutomer;同时Cutomer的age要大于20且gender要是male;第二个pattern也有三个约束,分别是:对象类型必须是Order,同时Order对应的Cutomer必须是前面的那个Customer且当前这个Order的price要大于1000。在这两个pattern没有符号连接,在Drools当中在pattern中没有连接符号,那么就用and来作为默认连接,所以在该规则的LHS部分中两个pattern只有都满足了才会返回true。默认情况下,每行可以用“;”来作为结束符(和Java的结束一样),当然行尾也可以不加“;”结尾。 2.2.1.1. 约束连接 对于对象内部的多个约束的连接,可以采用“&&”(and)、“||”(or)和“”(and)来实现, 代码清单2-7中规则的LHS部分的两个pattern就里对象内部约束就采用“”来实现,“&&” (and)、“||”(or)和“”这三个连接符号如果没有用小括号来显示的定义优先级的话,那么 它们的执行顺序是:“&&”(and)、“||”(or)和“”。代码清单2-8演示了使用“&&”(and)、 “||”(or)来连接约束的情形。 代码清单 2-8: rule "name" when Customer(age20 || gender==’male’&& city==’sh’); then end 代码清单2-8中规则的LHS部分只有一个pattern,在这个pattern中有四个约束,首先 必须是一个Customer对象,然后要么该对象gender=’male’且city=’sh’,要么age20,在Customer对象的字段约束当中,age20和gender=’male’且city=’sh’这两个有一个满足就可以了,这是因为“&&”连接符的优先级要高于“||”,所以代码清单2-8中规则的LHS部分pattern也可以写成代码清单2-9的样子,用一小括号括起来,这样看起来就更加直观了。 代码清单 2-9 rule "name" when Customer(age20 || (gender==’male’&& city==’sh’)) then end 表面上看“”与“&&”具有相同的含义,但是有一点需要注意,“,”与“&&”和“||” 不能混合使用,也就是说在有“&&”或“||”出现的LHS当中,是不可以有“,”连接符出 现的,反之亦然。 2.2.1.2. 比较操作符 在前面规则例子当中,我们已经接触到了诸如“”、“= =”之类的比较操作符,在Drools5 当中共提供了十二种类型的比较操作符,分别是:、=、、、= =、!=、contains、not contains、 memberof、not memberof、matches、not matches;在这十二种类型的比较操作符当中,前六个是比较常见也是用的比较多的比较操作符,本小节当中将着重对后六种类型的比较操作符进行介绍。 2.2.1.2.1. contains 比较操作符contains是用来检查一个Fact对象的某个字段(该字段要是一个Collection 或是一个Array类型的对象)是否包含一个指定的对象。代码清单2-10演示了contains比较 操作符的用法。 代码清单 2-10 package test rule "rule1" when $order:Order(); $customer:Customer(age 20orders contains $order); then System.out.println($customer.getName()); End contains操作符的语法如下: Object(field[Collection/Array] contains value) contains只能用于对象的某个Collection/Array类型的字段与另外一个值进行比较,作为 比较的值可以是一个静态的值,也可以是一个变量(绑定变量或者是一个global对象),在代 码清单2-10当中比较值$order就是一个绑定变量。 2.2.1.2.2. not contains not contains作用与contains作用相反,not contains是用来判断一个Fact对象的某个字 段(Collection/Array类型)是不是包含一个指定的对象,和contains比较符相同,它也只能 用在对象的field当中,代码清单2-11演示了not contains用法。\ 代码清单 2-11: package test rule "rule1" when $order:Order(items not contains "手机"); then System.out.println($customer.getName()); End 代码清单2-11的规则当中,在判断订单(Order)的时候,要求订单当中不能包含有“手 机”的货物,这里的规则对于比较的项就是一个表态固定字符串。 2.2.1.2.3. memberOf memberOf是用来判断某个Fact对象的某个字段是否在一个集合(Collection/Array)当 中,用法与contains有些类似,但也有不同. memberOf的语法如下: Object(fieldName memberOf value[Collection/Array]) 可以看到memberOf中集合类型的数据是作为被比较项的,集合类型的数据对象位于 memberOf操作符后面,同时在用memberOf比较操作符时被比较项一定要是一个变量(绑定 变量或者是一个global对象),而不能是一个静态值。代码清单2-12是一个演示memberOf 使用的规则示例。 代码清单 2-12: package test global String[] orderNames; rule "rule1" when $order:Order(name memberOf orderNames); then System.out.println($order.getName()); End 代码清单2-12中被比较对象是一个String Array类型的global对象。 2.2.1.2.4. not memberOf 该操作符与memberOf作用洽洽相反,是用来判断Fact对象当中某个字段值是不是中 某个集合(Collection/Array)当中,同时被比较的集合对象只能是一个变量(绑定变量或global 对象),代码清单2-13演示了not memberOf的用法。 代码清单 2-13 package test import java.util.List; rule "rule1" when $orderList:String[](); $order:Order(name not memberOf $orderList); then System.out.println($order.getName()); end 代码清单2-13中表示只有订单(Order)的名字(name)在订单集合($orderList)时 LHS才能返回true,该例子当中被比较的集合$orderList是一个字符串数组的类型的绑定 变量对象。 2.2.1.2.5. matches matches是用来对某个Fact的字段与标准的Java正则表达式进行相似匹配,被比较的字 符串可以是一个标准的Java正则表达式,但有一点需要注意,那就是正则表达式字符串当 中不用考虑“\”的转义问题。matches使用语法如下: 代码清单2-14演示了matches的用法 代码清单 2-14 package test import java.util.List; rule "rule1" when $customer:Customer(name matches "李.*"); then System.out.println($order.getName()); end 在清单代码清单2-14中示例的规则就像我们展示了matches的用法,该规则是用来查 找所有Customer对象的name属性是不是以“李”字开头,如果满足这一条件那么就将该 Customer对象的name属性打印出来,代码清单2-15是对该规则的测试类源码。 代码清单 2-15 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import com.test.Customer; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); Customer cus1 = new Customer(); cus1.setName("张三"); Customer cus2 = new Customer(); cus2.setName("李四"); Customer cus3 = new Customer(); cus3.setName("王二"); Customer cus4 = new Customer(); cus4.setName("李小龙"); statefulSession.insert(cus1); statefulSession.insert(cus2); statefulSession.insert(cus3); statefulSession.insert(cus4); statefulSession.fireAllRules(); statefulSession.dispose(); } } 测试类运行结果如图2-1所示。 图 2-1 从测试结果中可以看到,该规则运用matches操作符已经将所有的以“李”字开头的 Customer对象找到并打印出来。 2.2.1.2.6. not matches 与matches作用相反,是用来将某个Fact的字段与一个Java标准正则表达式进行匹配, 看是不是能与正则表达式匹配。not matches使用语法如下: Object(fieldname not matches “正则表达式”) 因为not matches用法与matches一样,只是作用相反,所以这里就不再举例了,有兴 趣的读者可以用not matches写个例子测试一下。 2.2.2. 结果部分 条件部分又被称之为Right Hand Side,简称为RHS,在一个规则当中then后面部分就 是RHS,只有在LHS的所有条件都满足时RHS部分才会执行。 RHS部分是规则真正要做事情的部分,可以将因条件满足而要触发的动作写在该部分 当中,在RHS当中可以使用LHS部分当中定义的绑定变量名、设置的全局变量、或者是直 接编写Java代码(对于要用到的Java类,需要在规则文件当中用import将类导入后方能使 用,这点和Java文件的编写规则相同)。 我们知道,在规则当中LHS就是用来放置条件的,所以在RHS当中虽然可以直接编写 Java代码,但不建议在代码当中有条件判断,如果需要条件判断,那么请重新考虑将其放在 LHS当中,否则就违背了使用规则的初衷。 在Drools当中,在RHS里面,提供了一些对当前Working Memory实现快速操作的宏 宏函数或对象,比如insert/insertLogical、update和retract就可以实现对当前Working Memory 中的Fact对象进行新增、删除或者是修改;如果您觉得还要使用Drools当中提供的其它方 法,那么您还可以使用另一外宏对象drools,通过该对象可以使用更多的操作当前Working Memory的方法;同时Drools还提供了一个名为kcontext的宏对象,使我们可以通过该对象 直接访问当前Working Memory的KnowledgeRuntime。下面我们就来详细讨论一下这些宏函 数的用法。 2.2.2.1. insert 函数insert的作用与我们在Java类当中调用StatefulKnowledgeSession对象的insert方法 的作用相同,都是用来将一个Fact对象插入到当前的Working Memory当中。它的基本用法 格式如下: insert(new Object()); 一旦调用insert宏函数,那么Drools会重新与所有的规则再重新匹配一次,对于没有设 置no-loop属性为true的规则,如果条件满足,不管其之前是否执行过都会再执行一次,这 个特性不仅存在于insert宏函数上,后面介绍的update、retract宏函数同样具有该特性,所 以在某些情况下因考虑不周调用insert、update或retract容易发生死循环,这点大家需要注 意。 代码清单2-16演示了insert函数的用法。 代码清单 2-16 package test import java.util.List; rule "rule1" salience 2 when eval(true); then Customer cus=new Customer(); cus.setName("张三"); insert(cus); end rule "rule2" salience 1 when $customer:Customer(name =="张三"); then System.out.println("rule2----"+$customer.getName()); end 代码清单2-16中有两个规则rule1和rule2,在rule1这个规则当中,LHS是eval(true), 表示没有任何条件限制(关于eval的用法,在后续的内容当中会有详细的介绍),RHS当中 创建了一个Customer对象,并设置它的name属性为“张三”,完成后调用insert宏函数将 其插入到当前的Working Memory当中;在名为rule2这个规则当中,首先判断当前的Working Memory当中没有一个name属性的值为“张三”的Customer对象,如果有的话,那么就在 其RHS部分将这个Customer对象的name属性打印出来。 很明显我们希望rule1这个规则先执行,rule2这个规则后执行,为了达成这个希望,我 们为这两个规则添加了一个名为“salience”的属性,该属性的作用是通过一个数字来确认 规则执行的优先级,数字越大,执行越靠前。这里为rule1规则设置它的salience属性值为2,rule2为1那么就表示rule1会先执行,rule2会后执行。实际上,我们前面讲到,因为一旦调用insert、update或retract函数,Drools会重新与所有的规则再重新匹配一次,对于没有设置no-loop属性为true的规则,如果条件满足,不管其之前是否执行过都会再执行一次。 所以这里的优先级如果设置也可以达到相同的效果。 编写测试类,测试这两个规则的执行情况,测试类源码如代码清单2-17所示。 代码清单 2-17 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); statefulSession.fireAllRules(); statefulSession.dispose(); } } 运行之后,结果如图2-3所示。 从执行的结果可以看出,正如我们前面论述的那样,rule1先执行,向Working Memory 当中插入一个name属性为“张三”的Customer对象,然后rule2再执行,将该对象的name 属性在控制台打印出来。 2.2.2.2. insertLogical insertLogical作用与insert类似,它的作用也是将一个Fact对象插入到当前的Working Memroy当中, 2.2.2.3. update update函数意义与其名称一样,用来实现对当前Working Memory当中的Fact进行更新, update宏函数的作用与StatefulSession对象的update方法的作用基本相同,都是用来告诉当前的Working Memory该Fact对象已经发生了变化。它的用法有两种形式,一种是直接更新一个Fact对象,另一种为通过指定FactHandle来更新与指定FactHandle对应的Fact对象, 下面我们就来通过两个实例来说明update的这两种用法。 先来看第一种用法,直接更新一个Fact对象。第一种用法的格式如下: update(new Object()); 代码清单 2-18 package test import java.util.List; query "query fact count" Customer(); end rule "rule1" salience 2 when eval(true); then Customer cus=new Customer(); cus.setName("张三"); cus.setAge(1); insert(cus); end rule "rule2" salience 1 when $customer:Customer(name=="张三"age<10); then $customer.setAge($customer.getAge()+1); update($customer); System.out.println("----------"+$customer.getName()); End 在代码清单2-18当中,有两个规则:rule1和rule2,在rule1当中我们通过使用insert 宏函数向当前Working Memory当中插入了一个name属性为“张三”、age属性为“1”的 Customer对象;rule2的规则当中首先判断当前的Working Memory当中有没有一个name属性为“张三”同时age属性值小于“10”的Customer,如果有的话,那么就重新设置当前这个Customer对象的age属性的值:将当前age属性的值加1,然后调用update宏函数,对这个修改后的Customer对象进行更新。 因为rule1的优先级为2,所以它会先执行;rule2的优先级为1,所以它会在rule1执行完成后执行。在第一次rule1规则执行完成后,Working Memory当中就会有一个一个name 属性为“张三”、age属性为“1”的Customer对象,正好满足rule2规则的条件,所以rule2 的RHS部分会执行,在rule2当中一旦使用update宏函数对Customer对象进行了更新,Drools 会重新检查所有的规则,看看有没有条件满足,这时只要Customer对象的age属性值在没 有更新的10之前rule2都会再次触发,直到Customer对象的age属性值更新到大于等于10, 这时所有的规则执行才算完成。 为了测试在多次调用update宏函数更新Customer对象后Working Memory当中还只存 在一个Customer对象,所以我们还添加了一个名为“query fact count”的query查询(关于 query查询后面的章节会有详细介绍),该查询的作用是用来检索当中Working Memory当中 有多少个Working Memory对象,在该示例当中,Customer对象应该只有一个。 编写测试类,测试这两个规则执行是不是符合我们的期望。测试类代码如代码清单2-19 所示。 代码清单 2-19 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.rule.QueryResults; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); statefulSession.fireAllRules(); statefulSession.dispose(); QueryResults qr = statefulSession.getQueryResults("query factcount"); System.out.println("customer 对象数目:" + qr.size()); System.out.println("end....."); } } 运行测试类,结果如图2-2所示。 从运行结果图中可以看出,Customer对象的name属性共输出了9次,同时最后检索出 来的Customer对象的结果也只是一个,符合我们的预期。 在上面的这个例子当中,一旦使用update宏函数,那么符合条件的规则又会重新触发, 不管该规则是否执行过,如果您希望规则只执行一次,那么可以通过设置规则的no-loop属 性为true来实现,如上面示例当中的rule2规则,如果添加no-loop属性为true,那么Customer 的name属性将只会输出一次。添加no-loop属性后的rule2规则如代码清单2-20所示。 代码清单 2-20 rule "rule2" salience 1 no-loop true when $customer:Customer(name=="张三"age then $customer.setAge($customer.getAge()+1); update($customer); System.out.println("----------"+$customer.getName()); End 再次运行测试类,可以得到如图2-3所示的结果。 可以看到,由于添加了no-loop属性为true,rule2规则只执行了一次。下面我们就来看 一下update宏函数的第二种用法。 第二种用法的格式如下: update(new FactHandle()new Object()); 从第二种用法格式上可以看出,它可以支持创建一个新的Fact对象,从而把FactHandle 对象指定的Fact对象替换掉,从而实现对象的全新更新,代码清单2-21里的规则演示这种 用法。 代码清单 2-21 package test import java.util.List; query "query fact count" Customer(); end rule "rule1" salience 2 when eval(true); then Customer cus=new Customer(); cus.setName("张三"); cus.setAge(1); insert(cus); end rule "rule2" salience 1 when $customer:Customer(name=="张三"age then Customer customer=new Customer(); customer.setName("张三"); customer.setAge($customer.getAge()+1); update(drools.getWorkingMemory().getFactHandleByIdentity($customer)customer); System.out.println("----------"+$customer.getName()); end 和前面的规则相比,更改了rule2的RHS部分,在这个部分当中,创建了一个新的 Customer对象,并设置了它的name属性为“张三”、age属性为当前Working Memory当中 的Customer对象的age属性值加1,设置完成后将这个新的Customer对象通过使用update 方法替换了原来的在Working Memory当中的Customer对象。编写测试类,可以发现运行 结果同前面的结果相同。 在这个规则当中我们使用了一个名为drools的宏对象,通过该对象获取当前的Working Memory对象,再通过WorkingMemory得到指定的Fact对象的FactHandle。关于drools宏 函数后面会有详细介绍。 2.2.2.4. retract 和StatefulSession的retract方法一样,宏函数retract也是用来将Working Memory当中 某个Fact对象从Working Memory当中删除,下面就通过一个例子来说明retract宏函数的用 法。代码清单2-22为示例规则。 代码清单 2-22 package test import java.util.List; query "query fact count" Customer(); end rule "rule1" salience 2 when eval(true); then Customer cus=new Customer(); cus.setName("张三"); cus.setAge(1); insert(cus); end rule "rule2" salience 1 when $customer:Customer(name=="张三"); then retract($customer); end 代码清单2-22中有两个规则和前面的示例基本相同,在rule2当中RHS部分,通过使 用retract宏函数对符合条件的Customer对象进行了删除,将其从当前的Working Memory 当中清除,这样在执行完所有的规则之后,调用名为“query fact count”的query查询,查 询到的结果数量应该是0,编写测试类,验证我们推理的结果。测试类代码如代码清单2-23 所示。 代码清单 2-23 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.rule.QueryResults; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); statefulSession.fireAllRules(); statefulSession.dispose(); QueryResults qr = statefulSession.getQueryResults("query factcount"); System.out.println("customer 对象数目:" + qr.size()); System.out.println("end....."); } } 运行测试类,可以看到如图2-4所示的结果。 正如推理的那样,因为使用了retract将Customer对象从当前的Working Memory当中 删除,所以运行结果中看到的Customer对象的数目为0。 2.2.2.5. drools 如果您希望在规则文件里更多的实现对当前的Working Memory控制,那么可以使用 drools宏对象实现,通过使用drools宏对象可以实现在规则文件里直接访问Working Memory。在前面介绍update宏函数的时候我们就使用drools宏对象来访问当前的Working Memory,得到一个指定的Fact对象的FactHandle。同时前面介绍的insert、insertLogical、 update和retract宏函数的功能皆可以通过使用drools宏对象来实现。代码清单2-24和代码 清单2-25所示的两个规则在功能上是完全一样的。 代码清单 2-24 rule "rule1" salience 11 when eval(true); then Customer cus=new Customer(); cus.setName("张三"); insert(cus); end 代码清单 2-25 rule "rule1" salience 11 when eval(true); then Customer cus=new Customer(); cus.setName("张三"); drools.insert(cus) end 代码清单2-24中向当前的Working Memory当中插入一个Customer对象采用的是宏函 数insert实现的,代码清单2-25则是采用宏对象drools的insert方法来实现,两个规则向当 前Working Memory当中插入对象的方法不同,但最终实现的功能是一样的。 使用drools宏对象,可以得到很多操纵Working Memory的方法,如果想查看drools宏 对象具有哪些方法可以使用,可以通过按“ALT”+“/”这两个功能键实现。具体做法是先 输入“drools.”然后按“ALT”+“/”这两个功能键就可以看到drools宏函数的方法列表, 如果在操作过程中,您没有看到方法列表,那应该是您操作系统里的 “ALT”+“/”这两 个功能键组合被其它软件占用。表2-1中罗列了drools宏对象的常用方法。 表格 2-1 方法名称 用法格式 含义 getWorkingMemory() drools.getWorkingMemory() 获取当前的WorkingMemory对象 halt() drools.halt() 在当前规则执行完成后,不再执行 其它未执行的规则。 getRule() drools.getRule() 得到当前的规则对象 insert(new Object) drools.insert(new Object) 向当前的WorkingMemory当中插入指定的对象,功能与宏函数insert 相同。 update(new Object) drools.update(new Object) 更新当前的WorkingMemory中指定的对象,功能与宏函数update相同。 update(FactHandle Object) drools.update(FactHandle Object) 更新当前的WorkingMemory中指定的对象,功能与宏函数update相同。 retract(new Object) drools.retract(new Object) 从当前的WorkingMemory中删除指 定的对象,功能与宏函数retract相 同。 2.2.2.6. kcontext kcontext也是Drools提供的一个宏对象,它的作用主要是用来得到当前的KnowledgeRuntime对象,KnowledgeRuntime对象可以实现与引擎的各种交互,关于KnowledgeRuntime对象的介绍,您可以参考后面的章节内容。 2.2.2.7. modify modify是一个表达式块,它可以快速实现对Fact对象多个属性进行修改,修改完成后 会自动更新到当前的Working Memory当中。它的基本语法格式如下: modify(fact-expression){ 修改Fact属性的表达式[修改Fact属性的表达式*] } 下面我们通过一个实例来说明modify表达式块的用法。 代码清单2-26中规则rule1里用使用modify表达式块来对Customer对象的属性进行了 修改。 代码清单 2-26 package test import java.util.List; rule "rule1" salience 2 when $customer:Customer(name=="张三"age==20); then System.out.println("modify before customer id:"+$customer.getId()+";age:"+$customer.getAge()); modify($customer){ setId("super man") setAge(30) } end rule "rule2" salience 1 when $customer:Customer(name=="张三"); then System.out.println("modify after customer id:"+$customer.getId()+";age:"+$customer.getAge()); end 在这两个规则当中,我们通过使用salience属性来控制他们的执行顺序,让rule1先执 行,执行时先打印出当前Customer对象的id与age属性的值,然后利用modify块对Customer 对象的这两个属性进行修改,接下来再执行rule2,再次打印Customer对象的id与age属性 的值,这时的值应该是modify块修改后的值。编写测试类,测试类代码如代码清单2-27所 示。 代码清单 2-27 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import com.test.Customer; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); Customer cus = new Customer(); cus.setAge(20); cus.setId("ZhangeShan"); cus.setName("张三"); statefulSession.insert(cus); statefulSession.fireAllRules(); statefulSession.dispose(); System.out.println("end....."); } } 运行测试类,可以看到如图2-4所示的结果。 这里有一点需要注意,那就是和insert、update、retract对Working Memory的操作一样, 一旦使用了modify块对某个Fact的属性进行了修改,那么会导致引擎重新检查所有规则是 否匹配条件,而不管其之前是否执行过,所以这个例子当中,在rule1这个规则当中,LHS 条件约束部分我们添加了两个条件name = = “张三”和age = = 20,如果去掉age = =20那么就会形成死循环,至于原因应该是比较容易理解的。 2.2.3. 属性部分 规则属性是用来控制规则执行的重要工具,在前面举出的关于规则的例子当中,已经 接触了如控制规则执行优先级的salience,和是否允许规则执行一次的no-loop等。在目前 的Drools5当中,规则的属性共有13个,它们分别是:activation-group、agenda-group、 auto-focus、date-effective、date-expires、dialect、duration、enabled、lock-on-active、no-loop、 ruleflow-group、salience、when,这些属性分别适用于不同的场景,下面我们就来分别介绍 这些属性的含义及用法。 2.2.3.1. salience 该属性的作用我们在前面的内容也有涉及,它的作用是用来设置规则执行的优先级, salience属性的值是一个数字,数字越大执行优先级越高,同时它的值可以是一个负数。默 认情况下,规则的salience默认值为0,所以如果我们不手动设置规则的salience属性,那 么它的执行顺序是随机的。 代码清单 2-28 package test rule "rule1" salience 1 when eval(true) then System.out.println("rule1"); end rule "rule2" salience 2 when eval(true) then System.out.println("rule2"); end 在代码清单2-28中两个规则,虽然rule1位于前面,但因为它的salience为1,而rule2 的salience属性为2,所以rule2会先执行,然后rule1才会执行。 2.2.3.2. no-loop 在一个规则当中如果条件满足就对Working Memory当中的某个Fact对象进行了修改, 比如使用update将其更新到当前的Working Memory当中,这时引擎会再次检查所有的规则 是否满足条件,如果满足会再次执行,代码清单2-29中的规则演示了这种用法。 代码清单 2-29 package test rule "rule1" salience 1 when $customer:Customer(name=="张三") then update($customer); System.out.println("customer name:"+$customer.getName()); end 编写测试类,测试类代码如代码清单2-30所示。 代码清单 2-30 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import com.test.Customer; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType. DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); Customer cus = new Customer(); cus.setName("张三"); statefulSession.insert(cus); statefulSession.fireAllRules(); statefulSession.dispose(); System.out.println("end....."); } } 运行测试代码,可以看到,控制台会不断输出打印Custom的name属性值的信息,而 且永无止境,很明显这是一个死循环。造成这个死循环的原因就是在代码清单2-29中规则 的RHS中使用了update宏函数,对当前的Customer的Fact对象进行更新操作,这样就导 致引擎再次重新检查所有的规则是否符合条件,如果符合条件,那么就继续执行,那么再次 对Customer进行更新……。 如何避免这种情况呢,这时可以引入no-loop属性来解决这个问题。no-loop属性的作用是用来控制已经执行过的规则在条件再次满足时是否再次执行,前面的示例当中也用到该属性帮我们解决应该当中的问题。no-loop属性的值是一个布尔型,默认情况下规则的no-loop属性的值为false,如果no-loop属性值为true,那么就表示该规则只会被引擎检查一次,如果满足条件就执行规则的RHS部分,如果引擎内部因为对Fact更新引起引擎再次启动检查规则,那么它会忽略掉所有的no-loop属性设置为true的规则。 现在对规则进行修改,为其添加一个no-loop属性,属性的值为true,修改后的规则如 代码清单2-31所示。 代码清单 2-31 package test rule "rule1" salience 1 no-loop true when $customer:Customer(name=="张三") then update($customer); System.out.println("customer name:"+$customer.getName()); end 再次运行测试类,可以看到控制台只会打印一次Custom的name属性值的信息。 2.2.3.3. date-effective 该属性是用来控制规则只有在到达后才会触发,在规则运行时,引擎会自动拿当前操作 系统的时候与date-effective设置的时间值进行比对,只有当系统时间=date-effective设置的 时间值时,规则才会触发执行,否则执行将不执行。在没有设置该属性的情况下,规则随时 可以触发,没有这种限制。 date-effective的值为一个日期型的字符串,默认情况下,date-effective可接受的日期格 式为“dd-MMM-yyyy”,例如2009年9月25日在设置为date-effective的值时,如果您的操 作系统为英文的,那么应该写成“25-Sep-2009”;如果是英文操作系统“25-九月-2009”,那 么就是,代码清单2-32就演示了date-effective属性的用法。 代码清单 2-32 package test rule "rule1" date-effective " 25-九月-2009" when eval(true); then System.out.println("rule1 is execution!"); end 该规则里的date-effective的属性值为“09-九月-2009”,因为我的操作系统是中文,所 以这里直接用的汉字来表示九月。 在实际使用的过程当中,如果您不想用这种时间的格式,那么可以在调用的Java代码 中通过使用System.setProperty(String keyString value)方法来修改默认的时间格式,如代码清 单2-33的规则就是采用格式化后的日期。 代码清单 2-33 package test rule "rule1" date-effective "2009-09-25" when eval(true); then System.out.println("rule1 is execution!"); end 在编写测试代码时,需要调用System.setProperty(String keyString value)方法来修改默认 的时间格式,调用的测试类如代码清单2-34所示。 代码清单 2-34 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); System.setProperty("drools.dateformat", "yyyy-MM-dd"); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); statefulSession.fireAllRules(); statefulSession.dispose(); System.out.println("end....."); } } 测试代码中System.setProperty("drools.dateformat"yyyy-MM-dd);这句就是用来修改当 前系统默认的时间格式的。在进行这个操作的时候有两个地方需要注意:一是设置的key必 须是“drools.dateformat”,值的话遵循标准的Java日期格式;二是修改当前系统默认的时间格式的这句代码必须放在向KnowledgeBuilder里添加规则文件之前,否则将不起作用。 2.2.3.4. date-expires 该属性的作用与date-effective属性恰恰相反, date-expires的作用是用来设置规则的有 效期,引擎在执行规则的时候,会检查规则有没有date-expires属性,如果有的话,那么会 将这个属性的值与当前系统时间进行比对,如果大于系统时间,那么规则就执行,否则就不 执行。该属性的值同样也是一个日期类型,默认格式也是“dd-MMM-yyyy”,具体用法与 date-effective属性相同。 代码清单2-35演示了date-expires属性的用法。 代码清单 2-35 package test rule "rule1" date-expires "2009-09-27" when eval(true); then System.out.println("rule1 is execution!"); end 在代码清单2-35中,名为rule1的规则的有效期为“2009-09-27”,当然该值遵循的是 “yyyy-MM-dd”格式,所以在执行该规则的时候需要设置drools.dateformat环境变量的值。 2.2.3.5. enabled enabled属性比较简单,它是用来定义一个规则是否可用的。该属性的值是一个布尔值, 默认该属性的值为true,表示规则是可用的,如果手工为一个规则添加一个enabled属性, 并且设置其enabled属性值为false,那么引擎就不会执行该规则。 2.2.3.6. dialect 该属性用来定义规则当中要使用的语言类型,目前Drools5版本当中支持两种类型的语 言:mvel和java,默认情况下,如果没有手工设置规则的dialect,那么使用的java语言。 代码清单2-36演示了如何查看当前规则的dialect的值。 代码清单 2-36 package test rule "rule1" when eval(true) then System.out.println("dialect:"+drools.getRule().getDialect()); End 代码清单2-36中名为rule1的规则RHS当中利用规则当中内置的宏对象drools得到当 前的Rule对象,再通过这个Rule对象得到其dialect属性的值。 执行该规则我们可以看如图2-5所示的结果。 从执行输出的结果当中可以看出,规则默认情况下使用的dialect值为“java”。 我们知道,规则是存放于规则文件当中,在规则文件当中也可以定义一个dialect属性, 如果一个规则没有定义dialect属性,那么它就采用规则文件里设置的dialect属性的值。规 则文件里dialect属性默认值为“java”,所以规则中默认dialect属性的值也为“java”。java 语言的语法大家都比较清楚,这里就简单的介绍一下mvel的语法。 mvel在本书当中有专门的章节加一介绍, 2.2.3.7. duration 对于一个规则来说,如果设置了该属性,那么规则将在该属性指定的值之后在另外一个 线程里触发。该属性对应的值为一个长整型,单位是毫秒,代码清单2-37里的规则rule1添加了duration属性,它的值为3000,表示该规则将在3000毫秒之后在另外一个线程里触发。 代码清单 2-37 package test rule "rule1" duration 3000 when eval(true) then System.out.println("rule thread id:"+Thread.currentThread().getId()); end 对应的测试Java类源码如代码清单2-38所示。 代码清单 2-38 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); statefulSession.fireAllRules(); statefulSession.dispose(); System.out.println("current threadid:" + Thread. currentThread().getId()); } } 运行测试类,可以得到如图2-6所示的结果。 从运行结果可以看到,执行规则和线程与测试类运行的线程的ID不同,表示规则不是 在与测试类的线程里运行的,同时,又因为规则执行产生了一个新的线程没有结束,所以可 以看到测试类的执行时一直处于未结束状态,需要手动结束主线程才行。 2.2.3.8. lock-on-active 当在规则上使用ruleflow-group属性或agenda-group属性的时候,将lock-on-action属性 的值设置为true,可能避免因某些Fact对象被修改而使已经执行过的规则再次被激活执行。 可以看出该属性与no-loop属性有相似之处,no-loop属性是为了避免Fact修改或调用 了insert、retract、update之类而导致规则再次激活执行,这里的lock-on-action属性也是起 这个作用,lock-on-active是no-loop的增强版属性,它主要作用在使用ruleflow-group属性 或agenda-group属性的时候。lock-on-active属性默认值为false。关于该属性的介绍,我们 在后面讲解规则流的时候还涉及到。 2.2.3.9. activation-group 该属性的作用是将若干个规则划分成一个组,用一个字符串来给这个组命名,这样在执 行的时候,具有相同activation-group属性的规则中只要有一个会被执行,其它的规则都将 不再执行。也就是说,在一组具有相同activation-group属性的规则当中,只有一个规则会 被执行,其它规则都将不会被执行。当然对于具有相同activation-group属性的规则当中究 竟哪一个会先执行,则可以用类似salience之类属性来实现。代码清单2-39就演示了 activation-group属性的用法。 代码清单 2-39 package test rule "rule1" activation-group "test" when eval(true) then ystem.out.println("rule1 execute"); end rule "rule 2" activation-group "test" when eval(true) then System.out.println("rule2 execute"); end 代码清单2-39中有两个规则:rule1和rule2,这两个规则具有相同的activation-group 属性,这个属性的值为“test”,前面我们讲过,具有相同activation-group属性的规则只会有一个被执行,其它规则将会被忽略掉,所以这里的rule1和rule2这两个规则因为具体相同名称的activation-group属性,所以它们只有一个会被执行。 2.2.3.10. agenda-group 规则的调用与执行是通过StatelessSession或StatefulSession来实现的,一般的顺序是创 建一个StatelessSession或StatefulSession,将各种经过编译的规则的package添加到session 当中,接下来将规则当中可能用到的Global对象和Fact对象插入到Session当中,最后调用 fireAllRules方法来触发、执行规则。在没有调用最后一步fireAllRules方法之前,所有的规 则及插入的Fact对象都存放在一个名叫Agenda表的对象当中,这个Agenda表中每一个规 则及与其匹配相关业务数据叫做Activation,在调用fireAllRules方法后,这些Activation会 依次执行,这些位于Agenda表中的Activation的执行顺序在没有设置相关用来控制顺序的 属性时(比如salience属性),它的执行顺序是随机的,不确定的。 Agenda Group是用来在Agenda的基础之上,对现在的规则进行再次分组,具体的分组 方法可以采用为规则添加agenda-group属性来实现。 agenda-group属性的值也是一个字符串,通过这个字符串,可以将规则分为若干个 Agenda Group,默认情况下,引擎在调用这些设置了agenda-group属性的规则的时候需要显 示的指定某个Agenda Group得到Focus(焦点),这样位于该Agenda Group当中的规则才会 触发执行,否则将不执行。 代码清单2-40当中有两个规则rule1和rule2,rule1的agenda-group属性值为001;rule2 的agenda-group属性值为002,这就表示rule1和rule2这两个规则分别被划分到名为001和002的两个Agenda Group当中,这样引擎执行这两个规则的时候必须显示的设置名为001 或002的Agenda Group得到焦点,这样位于001或002的Agenda Group里的规则才会触发 执行。 代码清单 2-40 package test rule "rule1" agenda-group "001" when eval(true) then System.out.println("rule1 execute"); end rule "rule 2" agenda-group "002" when eval(true) then System.out.println("rule2 execute"); end 为了测试这两个规则的执行情况,编写测试类,测试类代码如代码清单2-41所示。 代码清单 2-41 import java.util.Collection; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.base.RuleNameStartsWithAgendaFilter; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.rule.AgendaFilter; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); Collection collection = kb.getKnowledgePackages(); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(collection); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); statefulSession.getAgenda().getAgendaGroup("002").setFocus(); statefulSession.fireAllRules(); statefulSession.dispose(); } } 在代码清单2-41当中,注意如下这句: statefulSession.getAgenda().getAgendaGroup("002").setFocus(); 这句代码的作用是先得到当前的Agenda,再通过Agenda得到名为002的Agenda Group 对象,最后把Focus设置到名为002的Agenda Group当中,这个位于名为002的Agenda Group 中的rule2规则会执行,而位于名为001的Agenda Group当中的rule1则不会被执行,因为 名为001的Agenda Group没有得到Focus。 运行测试点,可以看到如图2-7所示。 实际应用当中agenda-group可以和auto-focus属性一起使用,这样就不会在代码当中显 示的为某个Agenda Group设置Focus了。一旦将某个规则的auto-focus属性设置为true,那 么即使该规则设置了agenda-group属性,我们也不需要在代码当中显示的设置Agenda Group 的Focus了。 2.2.3.11. auto-focus 前面我们也提到auto-focus属性,它的作用是用来在已设置了agenda-group的规则上设 置该规则是否可以自动独取Focus,如果该属性设置为true,那么在引擎执行时,就不需要 显示的为某个Agenda Group设置Focus,否则需要。 对于规则的执行的控制,还可以使用Agenda Filter来实现。在Drools当中,提供了一 个名为org.drools.runtime.rule.AgendaFilter的Agenda Filter接口,用户可以实现该接口,通 过规则当中的某些属性来控制规则要不要执行。org.drools.runtime.rule.AgendaFilter接口只有一个方法需要实现,方法体如下: public boolean accept(Activation activation); 在该方法当中提供了一个Activation参数,通过该参数我们可以得到当前正在执行的规 则对象或其它一些属性,该方法要返回一个布尔值,该布尔值就决定了要不要执行当前这个 规则,返回true就执行规则,否则就不执行。 代码清单2-42中的两个规则设置了agenda-group属性,设置了auto-focus属性为true, 这样在引擎在执行这两个规则的时候就不需要显示的为Agenda Group设置Focus。 代码清单 2-42 package test rule "rule1" agenda-group "001" auto-focus true when eval(true) then System.out.println("rule1 execute"); end rule "rule2" agenda-group "002" auto-focus true when eval(true) then System.out.println("rule2 execute"); end 在引擎执行规则的时候,我们希望使用规则名来对要执行的规则做一个过滤,此时就可 以通过AgendaFilter来实现,代码清单2-43既为我们实现的一个AgendaFilter类源码。 代码清单 2-43 import org.drools.runtime.rule.Activation; import org.drools.runtime.rule.AgendaFilter; public class TestAgendaFilter implements AgendaFilter { private String startName; public TestAgendaFilter(String startName) { this.startName = startName; } public boolean accept(Activation activation) { String ruleName = activation.getRule().getName(); if (ruleName.startsWith(this.startName)) { return true; } else { return false; } } } 从实现类中可以看到,我们采用的过滤方法是规则名的前缀,通过Activation得到当前 的Rule对象,然后得到当前规则的name,再用这个name与给定的name前缀进行比较, 如果相同就返回true,否则就返回false。 编写测试类,测试类代码如代码清单2-44所示。 代码清单 2-44 import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.impl.ClassPathResource; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.rule.AgendaFilter; public class Test { public static void main(String[] args) { KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder(); kb.add(new ClassPathResource("test/test.drl"), ResourceType.DRL); KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(kb.getKnowledgePackages()); StatefulKnowledgeSession statefulSession = knowledgeBase .newStatefulKnowledgeSession(); AgendaFilter filter = new TestAgendaFilter("rule1"); statefulSession.fireAllRules(filter); statefulSession.dispose(); } } 在测试类当中,给出的过滤规则名称的前缀是“rule1”,这样只要当前的Agenda当中 有名称以“rule1”开头的规则,那么就会执行,否则将不会被执行,本例子当中符合要求 的就是名为rule1的规则。运行测试类,运行结果如图2-8所示。 从测试结果来看,我们编写的AgendaFilter实现类起到了作用。 2.2.3.12. ruleflow-group 在使用规则流的时候要用到ruleflow-group属性,该属性的值为一个字符串,作用是用 来将规则划分为一个个的组,然后在规则流当中通过使用ruleflow-group属性的值,从而使 用对应的规则。后面在讨论规则流的时候还要对该属性进行详细介绍。 2.2.4. 注释 在编写规则的时候适当添加注释是一种好的习惯,好的注释不仅仅可以帮助别人理解你 编写的规则也可以帮助你自己日后维护规则。在Drools当中注释的写法与编写Java类的注 释的写法完全相同,注释的写法分两种:单行注释与多行注释。 2.2.4.1. 单行注释 单行注释可以采用“#”或者“//”来进行标记,如代码片段2-45中的规则就添加了两 个注释,一个以“//”开头,一个是以“#”开头。 代码清单 2-45 //规则rule1的注释 rule "rule1" when eval(true) #没有条件判断 then System.out.println("rule1 execute"); end 对于单行注释来说,您可以根据自己的喜好来选择注释的标记符,我个人的习惯所有的 单行注释全部采用“//”来实现,这样就和Java语法一致了。 2.2.4.2. 多行注释 如果要注释的内容较多,可以采用Drools当中的多行注释标记来实现。Drools当中的 多行注释标记与Java语法完全一样,以“/*”开始,以“*/”结束,代码清单2-46演示了 这种用法。 代码清单 2-46 /* 规则rule1的注释 这是一个测试用规则 */ rule "rule1" when eval(true) #没有条件判断 then System.out.println("rule1 execute"); end 代码清单2-46当中,在规则名称部分,添加了一个多行注释文本,用于表明该规则的 作用,实际应用当中我们推荐在每一个规则开始前添加这么一个多行注释,用于说明该规则 的作用。 2.3. 函数 函数是定义在规则文件当中一代码块,作用是将在规则文件当中若干个规则都会用到的 业务操作封装起来,实现业务代码的复用,减少规则编写的工作量。 函数的编写位置可以是规则文件当中package声明后的任何地方,Drools当中函数声明 的语法格式如代码清单2-47所示: 代码清单 2-47 function void/Object functionName(Type arg...) { /*函数体的业务代码*/ } Drools当中的函数以function标记开头,如果函数体没有返回值,那么function后面就 是void,如果有返回值这里的void要换成对应的返回值对象,接下来就是函数的名称函数 名称的定义可以参考Java类当中方法的命名原则,对于一个函数可以有若干个输入参数, 所以函数名后面的括号当中可以定义若干个输入参数。定义输入参数的方法是先声明参数类 型,然后接上参数名,这点和Java当中方法的输入参数定义是完全一样的,最后就是用“{…}” 括起来的业务逻辑代码,业务代码的书写采用的是标准的Java语法。 代码清单2-48演示了一个简单的函数的编写方法。 代码清单 2-48 function void printName(String name) { System.out.println("您的名字是:"+name); } 该函数的名称为printName,不需要返回值,它需要一个String类型的输入参数,函数 体的业务逻辑比较简单,将输入参数组合后在控制台打印出来。 可以看到Drools当中函数的定义与Java当中方法的定义极为相似,与Java当中定义方 法相比,唯一不同之处就是在Drools的函数定义当中没有可见范围的设定,而Java当中可 以通过public、private之类来设置方法的可见范围。在Drools当中函数的可见范围是当前的 函数所在的规则文件,位于其它规则文件当中的规则是不可以调用不在本规则文件当中的函 数的。所以Drools当中函数的可见范围可以简单将其与Java当中方法的private类型划上等 号。下面我们来看一个函数应用的例子,加深对Drools函数的理解。 代码清单2-49当中的规则文件包含一个函数和两个规则,在这两个规则当中都调用了 这个函数。 代码清单 2-49 package test import java.util.List; import java.util.ArrayList; /* 一个测试函数 用来向Customer对象当中添加指定数量的Order对象的函数 */ function void setOrder(Customer customer int orderSize) { List ls=new ArrayList(); for(int i=0;i

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享文档获得金币 ] 20 人已下载

下载文档