从今天开始,我们将分两期来详细的介绍Drools规则引擎的原理,和各关键类的使用方法。
1. 概述 :
Drools 分为两个主要部分:构建( Authoring )和运行时( Runtime )。
构建的过程涉及到 .drl 或 .xml 规则文件的创建,它们被读入一个解析器,使用 ANTLR 3 语法进行解析。解析器对语法进行正确性的检查,然后产生一种中间结构“ descr ”, descr 用 AST 来描述规则。 AST 然后被传到 PackageBuilder ,由 PackagBuilder 来产生 Packaged 对象。 PackageBuilder 还承担着一些代码产生和编译的工作,这些对于产生 Package 对象都时必需的。 Package 对象是一个可以配置的,可序列化的,由一个或多个规则组成的对象。下图阐明了上述过程:
![](https://i-blog.csdnimg.cn/blog_migrate/2f49057a67b2906935cc64fa5d527504.png)
Figure 1.1 Authoring Components
RuleBase 是一个运行时组件,它包含了一个或多个 Package 对象。可以在任何时刻将一个 Package 对象加入或移出 RuleBase 对象。一个 RuleBase 对象可以在任意时刻实例化一个或多个 WorkingMemory 对象,在它的内部保持对这些 WorkingMemory 的弱引用。 WorkingMemory 由一系列子组件组成。当应用程序中的对象被 assert 进 WorkingMemory ,可能会导致一个或多个 Activation 的产生,然后由 Agenda 负责安排这些 Activation 的执行。下图说明了上述过程:
![](https://i-blog.csdnimg.cn/blog_migrate/1d6ff3e7aad163cd09418b8496af467b.png)
2 .构建( Authoring ):
主要有三个类用来完成构建过程: DrlParser, XmlParser 和 PackageBuilder 。两个解析器类从传入的 Reader 实例产生 descr AST 模型。 PackageBuilder 提供了简便的 API ,使你可以忽略那两个类的存在。这两个简单的方法是:“ addPackageFromDrl ”和“ addPackageFromXml ”,两个都只要传入一个 Reader 实例作为参数。下面的例子说明了如何从 classpath 中的 xml 和 drl 文件创建一个 Package 对象。注意:所有传入同一个 PackageBuilder 实例的规则源,都必须是在相同的 package 命名空间( namespace )中。
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( " package1.drl " ) ) );
builder.addPackageFromXml( new InputStreamReader( getClass().getResourceAsStream( " package2.drl " ) ) );
Package pkg = builder.getPackage();
![](https://i-blog.csdnimg.cn/blog_migrate/51f283d0de178911c30bb317aabcbcab.png)
Figure 2.1 PackageBuilder
PackageBuilder 是可以配置的,使用 PackageBuilderConfiguration 。通常,你可以指定另一个 parent ClassLoader 和用什么编译器( compiler ),默认是 Eclipse JDT 。下面显示了如何指定 JANINO 编译器:
conf.setCompiler( PackageBuilderConfiguration.JANINO );
PackageBuilder builder = new PackageBuilder( conf );
![](https://i-blog.csdnimg.cn/blog_migrate/5ae08cd550884ef588eb986ca4d72f7d.png)
Figure 2.2 . PackageBuilderConfiguration
3 . RuleBase:
![](https://i-blog.csdnimg.cn/blog_migrate/1f88db9a7f4b2174f62b6bea1f4b04b3.png)
Figure 3.1 . RuleBase
一个 RuleBase 包含了多个将被使用的规则包( packages of rules )。一个 RuleBase 是可以序列化的,所以它可以被配置到 JNDI 或其他类似的服务。通常,第一次使用时,一个 RuleBase 被创建并缓存。 RuleBase 用 RuleBaseFactory 来实例化,默认返回一个 ReteOO RuleBase 。可以传入参数来指定采用 ReteOO 或 Leaps 。然后,用 addPackage 方法加入 Package 实例。你可以加入有相同命名空间( namespace )的多个 Package 。
ruleBase.addPackage(pkg);
![](https://i-blog.csdnimg.cn/blog_migrate/ca7cc27c8c1fee7ef9e016775945f731.png)
Figure 3.2. RuleBaseFactory
一个 rulebase instance 是线程安全的,所有你可以在你的应用中,让一个 rulebase instance 在多个线程中共享。对于一个 rulebase 的最通常的操作是产生一个新的 WorkingMemory 。
这个 rulebase 保持着到它所产生的 WorkingMemoryd 的弱引用,所以在长时间运行的 WorkingMemory 中,如果 rules 发生改变,这些 WorkingMemory 可以即使的根据最新的 rules 进行更新,而不必重启 WorkingMemory 。你也可以指定 RuleBase 不必保持一个弱引用,但是你要保证 RuleBase 不用更新。
ruleBase.newWorkingMemory( false ); // do not maintain a weak reference
任何时候, Package 可以被加入或移除;所有的改变都会被反映到现存的 WorkingMemory 中。不要忘了调用 fireAllRules() 让 Activations 激发。
ruleBase.removePackage( " org.com.sample " ); // remove a package, and all its parts, by it's namespace
ruleBase.removeRule( " org.com.sample " , " my rule " ); // remove a specific rule from a namespace
虽然有删除一个单独规则的方法,但是却没有加入一个单独规则的方法(要达到这个目的只有加入一个只有一条规则的 package )。
<!-- [if !supportEmptyParas]-->
RuleBaseConfigurator 可以指定 RuleBase 的附加行为。在加入 RuleBase 后, RuleBaseConfiguration 就变成不可变对象。
conf.setProperty( RuleBaseConfiguration.PROPERTY_ASSERT_BEHAVIOR,
RuleBaseConfiguration.WM_BEHAVIOR_EQUALITY );
RuleBase ruleBase = new ReteooRuleBase( conf );
两个主要的属性是: PROPERT_ASSERT_BEHAVIOR 和 PROPERTY_LOGICAL_OVERRIDE_BEHAVIOR (在以后的部分中会解释)。所有的属性值都是 RuleBaseConfiguration 类中的静态域常量。
Figure 3.3 RuleBaseConfiguration