Dashboard > SpringSide Wiki > HOME > JBossRules
  SpringSide Wiki Log In | Sign Up   View a printable version of the current page.  
  JBossRules
Added by SpringSideTeam, last edited by SpringSideTeam on 2006-12-23  (view change)
Labels: 
(None)

JBossRules 规则引擎指南

作者: 江南白衣Scheweigen,作者保留版权,转载请注明出处。

1.概述

1.1 框架介绍

  每当大家的业务逻辑复杂、变动频繁时,总会想起那个充满诱惑力的名词:规则引擎。但实际接触了之后,又会觉得规则引擎不过尔尔。

  所以,大家继续往下看本文之前,请先看一下JBoss Rules的Why use a Rule Engine,明白它的实际用途。

  Drools 变身为JBoss Rules 3.0后已经拥有了好得多规则语法,平民级的DSL语言和基于Eclipse的编辑器,前景明亮。

  其中平民级的DSL语法促使规则引擎这个有点神秘的高端技术,很有机会在2007年大面积应用于各个普通开发团队,如Spring,Hibernate一样平常。

1.2 入门参考资料

网站:http://labs.jboss.com/jbossrules

完整文档:http://labs.jboss.com/portal/jbossrules/docs/index.html

guangnian0412的中文学习笔记:http://www.blogjava.net/guangnian0412/category/11762.html

1.3 what SpringSide do

SpringSide参考JBoss Seam对JBossRules的封装,封装了DroolsTemplate基类,将JBossRules集成到Spring体系中。

在BookStore示例中,演示了DSL和纯规则语法两种方式进行促销计价。

2. 入门

2.1 一次Drools的规则运算

1. 从Drl规则文件编译得到RuleBase--编译后的规则集;

2. 从RuleBase生成本次规则运算的场地--WorkingMemory;

3. AssertObject--将规则运算用到的事实放入WorkingMemory;

4. FireAllRules,对WorkingMemory中的事实进行规则运算,对符合规则条件的事实进行操作。

2.2 Drools的规则文件

package org.springside.components.hellworld

import org.springside.components.jbossrules.DroolsTemplateTest.Message

rule "Hello World"
	when
		m : Message( status == Message.HELLO)
	then
		m.setMessage( "Goodbye cruel world" );
		m.setStatus( Message.GOODBYE );
end

   简单解释

   这个规则是找出WorkingMemory里status=HELLO的Message对象,将他们的状态改为GOODBYE.

   注意每条的处理方式就是这样--找出类型与属性符合的实体,对它进行处理。

   [package]是随便命名的一个Name Space,JBossRules提供Package级的管理API。

   [import] 规则引擎里用到的Java对象

   [when]的语法是Drools自定义的(简化了Java的API),m 代表 status=Message.HELLO的Message对象。

   [then]的语法是正宗Java 语法,每句以;分号结束,里面用到的对象必须在when语句或Gobal语句声明。

   也可以使用其它语言如Groovy来编写,但需要在编译规则时设定语言。

2.3 平民级的DSL映射语言

  JBossRules的DSL采用了充满农民式奸诈的直接映射法,而不是复杂的Yacc,Antrl之类的语义分析是我最喜欢的地方。

  唯有如此,普通团队才可以用上这对客户充满诱惑,在团队内部使用也能清晰规则定义的DSL。 而且,这DSL还有JBossRules-IDE(Eclipse 插件)的支持,能够进行语法提示。

  朴素的DSL定义

[when]total price more than {value} RMB=o : Order( totalPrice >={value})
[then]discount is {discount}%=pricingService.discount(o,{discount});

   将[when] 中的o : Order( totalPrice >= {value}) 映射为一句DSL:total price more than {value}RMB,其中 {value}做成了变量,可以随意设置价格的上限。

  使用DSL语法定义规则

package org.springside.bookstore.pricing

import org.springside.bookstore.commons.model.Order;
global org.springside.bookstore.components.jbossrules.OrderPricingService pricingService;
expander Pricing.dsl

rule "discount is 90%  when more than 100RMB"
	when
		total price more than 100 RMB
	then
		discount is 90%
end

2.4 JBossRules的Java API

  JBossRules的Java API大概分两类:

  一类是编译规则构建WorkingMemory的,使用SpringSide封装的DroolsTemplate后就不需要直接调用了,想了解的可以看JBossRules带的Sample。

  一类是运行规则的,来去主要是assertObject()放入事实 和 setGlobal()两个准备函数 和 fireAllRules() 执行函数。其他API请细看参考手册与JavaDOC。

3. SpringSide的DroolsTemplate

     SpringSide仿照JBoss Seam,将JBossRules集成到Spring里。

     DroolsTemplate不会无聊的封装所有API,而只会完成将定义在apllicationContext.xml中的规则文件编译成RuleBase,向用户返回WorkingMemory这一集成Spring的关键步骤。用户之后使用JBossRules的原装API继续操作。

     因为编译RuleBase需要一定的性能消耗,所以RuleBase作成一个lazy load的单例。

     根据JBossRules文档的说法与JBoss Seam的做法,它们连WorkingMemory也是重用的,所以assertObject(Object)时会先找WorkingMemory中有没有相同的Object,有则Modify,没有则Insert。

     示例代码:PricingService.java

WorkingMemory wm = droolsTemplate.getNewWorkingMemory(order);
wm.setGlobal("pricingService", pricingService);
wm.fireAllRules();

3.1  ApplicationContext.xml文件:

<bean id="pricingTemplate" class="org.springside.components.jbossrules.DroolsTemplate">
    <!-- 使用dsl的版本 -->
    <property name="dslFile" value="rules/pricing/Pricing.dsl"/>
    <property name="ruleFiles" value="rules/pricing/PricingWithDSL.drl"/>
</bean>

   注意dsl与drl文件路径的定义是Spring Style的,可以加上classpath*: file: 等前缀,可以使用ant-style的通配符,ruleFiles可以用,逗号分隔定义多个文件。

   3.2 DroolsTemplate API简介

1.public WorkingMemory getWorkingMemory(Object... assertObjects)

   由RuleBase生成WorkingMemory,再放入参数列表中的object,返回WorkingMemory。

   其中assertObjects是一个可变参数,支持getWorkingMemory(); getWorkingMemory(object1,object2); getWorkingMemory(new Object[] {object1,object2}); 多种形式的调用。

   workingMemory是DroolsTemplate的成员变量,从RuleBase生成后会重复使用,因此注入事实时会先找WorkingMemory中是否已存在该事实,有则修改而不放入新的事实。

2. public WorkingMemory getWorkingMemory(Object... assertObjects)

   不使用DroolsTemplate成员变量中的workingMemory,重新生成一个新的wm的版本。

3. public void assertObject(WorkingMemory workingMemory, Object element)

   往workingMemory中注入事实的函数,如果workingMemory中已存在该事实,进行更新。

4.Tips

4.1 使用Service类封装规则符合后操作

  使用Service封装规则符合后的操作,而不是将每一句操作都定义在[then]部分,可以让规则文件清晰简单。

比如

global org.springside.bookstore.components.jbossrules.OrderPricingService pricingService;

rule
  when
    o : Order( totalPrice >=100)
  then
    pricingService.discount(o,90);
end

比下面的代码清晰很多。

rule
  when
    o: Order( totalPrice >=100)
  then
    BigDecimal newPrice = new BigDecimal(o.getTotalPrice() * (discount / 100)).setScale(2, RoundingMode.HALF_UP);
    o.setTotalPrice(newPrice.doubleValue());
end

   封装的Service一般以gobal形式存在。

   在调用规则时以wm.setGlobal("pricingService", pricingService); 放入,命名为pricingService.

   在规则中以global org.springside.bookstore.components.jbossrules.OrderPricingService pricingService; 声明,名称必须与放入时相同,则处理函数里就能以pricingService 操作。

4.2 性能

  Drools 3.0 相对于2.5 以来,在效率方面有了大大的提升。现在的规则执行速度上面已经基本上达到了JRule6 的水平。

  对于执行效率,有几点需要注意的地方:

4.2.1、Condition 的排放顺序

    如果你的规则中Condition 不只一个的话,那么把哪个Condition 放在前面是很有讲究的,这直接关系到规则引擎的执行效率。

例如下面,有上万个如下类型的facts 将同时assert 进入规则引擎中参与计算。而这些facts 中绝大部分facts 的type 为1,name 却各不相同:

  则 fact : FactObject(type == 1, name == "test")  比 fact : FactObject(name == "test", type == 1),效率相差几十倍,其原理和数据库索引的原因相似。

4.2.2 对于字符串,如果在比较之前使用intern() 的话,那么效率也会部分提升

    参见JBoss Wiki:http://wiki.jboss.org/wiki/Wiki.jsp?page=StringIntern

4.3 语法限制

4.3.1 关键字冲突

   如果使用了任何与关键字相同的名称,包含包名、组名、dsl 定义中的单词,以及函数名称,那么这个drl 文件将会在构建时候报错。

   比如:package org.springside.rule 将报错 ,这个Bug 将在Drools 3.1M1 中解决。

   Drools 的所有关键字列出:
when then rule end contains matches and or modify retract assert salience function
query exists eval agenda-group no-loop duration -> not auto-focus

4.3.2 DSL中不能使用标点

   在dsl 定义中,不能使用标点。如:Order's price larger than 100。这样的定义也无法被编译。

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.2.9 Build:#527 2006-09-07) - Bug/feature request - Contact Administrators