Drools Runtime
主要介绍Drools的组件是如何工作的,它们各个组件的职责,和他们的配置,本章重点介绍如下:
• 了解Drools是怎么运行的
• 常用的配置
• 使用KieScanner加载动态的规则变化
为了生成规则引擎的新实例,我们需要理解一下概念
• KieServices
• KieContainer
• KieModule
• KieBase
• KieSession
我们先从KieServices开始,它是提供注册服务的,通过它可以访问到上面所有的类,我们用下面的静态方法调用就可以拿到它
KieServices kss = KieServices.Factory.get();
通过它,我们可以访问规则引擎里面的一系列工厂,服务和引擎.
Rule attributes
enable 它来控制规则不可用,该属性一般是动态去定义,比如全局变量传进来的值,或者某个属性
rule "attribute"
enabled false
agenda-group "group001"
when
then
System.out.println("group 001 is used");
end
salience 控制优先级,再同时满足条件的多个rule中,salience数值越大,执行的越靠前
rule "Classify Item - Mid/High Range (special)"
salience 10
when
$i: Item( cost > 300 && cost < 400 )
then
$i.setCategory(
Item.Category.SPECIAL_MIDHIGH_RANGE);
update($i);
end
activation-group 当同一个activation-group里面的规则有很多个并且同时满足条件的时候,drools只会执行它们中的其中一个
agenda-group 每一个rule都有一个agenda-group,默认的是MAIN,通过它可以给不同的业务场景设置不同的agenda-group,因为是按业务分组,所以经常会跟salience一起使用
no-loop 在某些场景下,比如一个规则被执行后,有一个修改操作,但是修改后的结果仍然满足当前的规则条件,那么又会执行该规则,最坏的情况就是进入死循环状态,这个属性就是告诉该规则只会被当前调用者执行一次
rule "Classify Item - Mid/High Range (special)"
no-loop
salience 10
when
$i: Item( cost > 300 && cost < 400 )
then
$i.setCategory(
Item.Category.SPECIAL_MIDHIGH_RANGE);
update($i);
end
agenda-group 规则分组,比如我们现实生活中的商城,有满减活动优惠规则,有积分优惠规则,有会员又会规则,通过它对规则进行分组,有利于去和相应的业务分开.
rule "Promotion: more than 10 pencils get 10% discount"
agenda-group "promotions"
when
OrderLine(item.name == "pencil", quantity > 10)
then
insert(new Discount(0.10));
end
KieSession ksession = ...;
ksession.getAgenda().getAgendaGroup("promotions").setFocus();
ksession.fireAllRules();
date-effective /date-expires 执行时间,有效期和过期时间,比如商城的假日优惠活动,这个时间段才会启动,其它时间段是不执行的,那么这个就到了很好的作用
rule "Add special tax of 3%"
date-effective "01-Jan-2015"
date-expires "31-Dec-2020"
when $i: Item()
then $i.setSalePrice($i.getSalePrice() * 1.03);
end
Lock-on-active 最简单的理解方式就是解决no-loop解决不了的事情,no-loop没办法阻止两个rule之间循环调用,代码如下, rule1执行完之后执行rule2,rule2执行完之后执行rule1,所以no-loop只能解决自身的循环调用,不能解决多个rule的相互循环调用.
rule "Give extra 2% discount for orders larger
no-loop true
when $o: Order(totalItems > 15)
then modify ($o) { increaseDiscount(0.02);
end
rule "Give extra 2% discount for orders larger
no-loop true
when $o: Order(total > 100.00)
then modify ($o) { increaseDiscount(0.02);
end
以下的写法可以解决no-loop解决不了的问题:
rule "Give extra 2% discount for orders larger than $100"
lock-on-active true
when $o: Order(total > 100.00)
then modify ($o) { increaseDiscount(0.02); }
end
通过属性去控制规则,在现实生活中,比如以下场景,优惠活动,有满100优惠和满5件商品优惠,但是此优惠是二选一不能同步享受,这时候可以通过实体类的属性来判断规则的执行(这个不可取,会造成实体类中出现一些额外的字段去存储这些值,然后增加维护成本):
rule "Add 2% discount for orders larger than $100"
when $o:
Order(total > 100.00, has100DollarsDiscount == false)
then
modify($o){
increaseDiscount(0.02);
setHas100DollarsDiscount(true);
}
end
rule "Add 2% discount for orders larger than 15 items"
when $o:
Order(total > 100.00, has15ItemsDiscount == false)
then
modify($o){
increaseDiscount(0.02);
setHas15ItemsDiscount(true);
}
end
Declared types 类型声明:在其内部,可以定义对象属性(比如String)和基础类型(如long), 他们可以继承其它的类型声明,它甚至可以申明一些自定义的实体类,你可以通过getter/setter(rule engine 自动生成)方法去访问它
package person;
import com.drools.entities.Person
dialect "mvel"
declare specialPerson extends Person
person: Person
company: String
level: int
end
java代码设置和获取值的方法
@Test
public void testDeclare() {
try {
FactType type = ks.getKieBase().getFactType(
"person", "specialPerson");
Object instance = type.newInstance();
type.set(instance, "company", "backstop");
type.set(instance, "level", 99);
Object company = type.get(instance, "company");
Object level = type.get(instance, "level");
System.out.println(company.toString());
System.out.println(level.toString());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
property-reactive beans 目前不是很清楚,英文中提到和no-loop和lock-on-active类似
package person.reactive;
dialect "mvel"
import com.drools.entities.Person
declare specialPerson extends Person
@propertyReactive
person: Person
company: String
level: int
end
rule "Larger than 20 items orders are special orders"
when
$o: specialPerson(
level > 3
) @Watch (!person)
then
modify ($o) {
setPerson(new Person("Depu", 99, Person.GENDER_MALE, ".."))
}
end
@Test
public void testReactive() throws Exception {
FactType type = ks.getKieBase().getFactType("person.reactive", "specialPerson");
Object person = type.newInstance();
// type.set(person, "company", "backstop");
type.set(person, "level", 5);
ks.insert(person);
int count = ks.fireAllRules();
System.out.println("total run " +count+ " rules");
Person result = (Person) type.get(person, "person");
System.out.println("result:" + result);
}
global 定义全局变量
global java.util.List list
rule "match all famale by from"
when
Company($persons: persons)
$p:Person(gender == Person.GENDER_FEMALE) from $persons
then
System.out.println("girl'name is " + $p.getName());
end
ks.setGlobal("list", persons);