<dependencies>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-coreartifactId>
<version>6.3.0.Finalversion>
dependency>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-compilerartifactId>
<version>6.3.0.Finalversion>
dependency>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-decisiontablesartifactId>
<version>6.3.0.Finalversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
内容如下:
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"/>
自此我们的环境就搭建完毕
package me.drools.demo.domain;
/** * 实体类 */
public class Applicant {
private String name;
private int age;
private boolean valid;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
public Applicant(String name, int age) {
this.name = name;
this.age = age;
}
public Applicant(){}
}
package com.company.license
import me.drools.demo.domain.*
dialect "mvel"
rule "Is of valid age"
when
$a : Applicant( age >= 18)
then
$a.setValid(true);
end
package
是必须要申明,但是无需与文件夹目录对应,主要是为了方便管理。
import
语言,跟JAVA的import语句用法一致,包括通配符(*)这些,不过他可以import类的方法。
rule
是关键字,后面跟规则名
when
后面是规则要满足的条件,本例中其实有2个条件要满足:
1. 当时判断的对象的类型必须是Applicant
2. age >= 18,age即Applicant的属性,用IDEA开发时,会有提示,eclipse没有试过
接着我们将当前满足上面2个条件的目标对象(Target) 绑定给$a, $不是必须的,但是推荐加上,以区别类的属性,方法。
then
后面表示满足条件时执行的操作,可以看我们,直接调用了setValid方法,置为True
end
表示结束
public class ApplicantTest {
@Test
public void testCheckAgeValid(){
KieServices kieService = KieServices.Factory.get();
KieContainer kieContainer = kieService.getKieClasspathContainer();
StatelessKieSession kSession = kieContainer.newStatelessKieSession();
Applicant applicant = new Applicant( "Mr John Smith", 20 );
Assert.assertFalse( applicant.isValid() );
kSession.execute(applicant);
Assert.assertTrue( applicant.isValid() );
}
}
运行Junit,2上断言都顺利通过,至此第一个Demo就完成。在测试类还有很多类我们不明白它的作用
在上一节中,我们讲了最一个最简单的例子,单个对象的实例。其实这个对象,在Drools中有个专业名词叫:Fact
。 Fact
对象的方式来实现规则引擎与业务数据的交互,对于Fact对象就是普通的具有若干个属性及其对应的getter与setter方法的JavaBean对象,所以注意啦,它不是我们业务Domain对象,他应该具备规则所涉及的一个或者多个Domain对象的多个属性。
另外在drools中KieSession分为2种:
从官方的说法来看,2者除了在处理大数据量时有一定区别外,平时几无区别,statelessSession也是基于statefulSession的,那么有状态的Session有以下几个常用的用途:
这一节我们就通过monitoring user case(监视器用法)讲讲多个对象的之间的相互协作(关联)。我们以房屋发生火灾报警时,蓬头自动出水灭火的场景来模拟监视器用法。
package me.drools.demo.monitoring;
public class Room {
private String name
// getter and setter methods here
}
public class Sprinkler {
private Room room;
private boolean on;
// getter and setter methods here
}
public class Fire {
private Room room;
// getter and setter methods here
}
public class Alarm {
}
定义monitoring.drl
package me.drools.monitoring
import me.drools.demo.monitoring.*
dialect "mvel"
rule "When there is a fire turn on the sprinkler"
when
Fire($room:room)
$sprinkler : Sprinkler(room == $room,on == false)
then
modify($sprinkler){setOn(true)}
System.out.println("Turn on the sprinkler for room " + $room.getName());
end
rule "When the fire is gone turn off the sprinkler"
when
$room : Room( )
$sprinkler : Sprinkler( room == $room, on == true )
not Fire( room == $room )
then
modify( $sprinkler ) { setOn( false ) };
System.out.println( "Turn off the sprinkler for room " + $room.getName() );
end
rule "Raise the alarm when we have one or more fires"
when
exists Fire()
then
insert( new Alarm() );
System.out.println( "Raise the alarm" );
end
rule "Cancel the alarm when all the fires have gone"
when
not Fire()
$alarm : Alarm()
then
delete( $alarm );
System.out.println( "Cancel the alarm" );
end
rule "Status output when things are ok"
when
not Alarm()
not Sprinkler( on == true )
then
System.out.println( "Everything is ok" );
end
这一部分其实没有啥好讲的,跟Java语法都是非常的接近,注意下几个关键字的用法:modify
,not
,exists
,delete
,insert
其实modify
语句也可以直接写成java方法调用,比如$sprinkler.setOn(true);
public class FireTest {
@Test
public void testFire(){
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
KieSession ksession = kContainer.newKieSession();
String[] names = new String[]{"kitchen", "bedroom", "office", "livingroom"};
Map name2room = new HashMap();
for( String name: names ){
Room room = new Room( name );
name2room.put( name, room );
ksession.insert( room );
Sprinkler sprinkler = new Sprinkler( room );
ksession.insert( sprinkler );
}
ksession.fireAllRules();
Fire kitchenFire = new Fire( name2room.get( "kitchen" ) );
Fire officeFire = new Fire( name2room.get( "office" ) );
FactHandle kitchenFireHandle = ksession.insert( kitchenFire );
FactHandle officeFireHandle = ksession.insert( officeFire );
ksession.fireAllRules();
ksession.delete( kitchenFireHandle );
ksession.delete( officeFireHandle );
ksession.fireAllRules();
}
}
通过运行Junit测试,对照drl文件,观察打印语句,包括规则执行的次数,感受上面提到的几个关键字的用法,以及如何通过这些关键字在rule之间相互流转。
其实就是笛卡尔乘积,主要涉及到我们在when
语句块里面条件表达式的编写,假设我们现在增加这样一个规则:
rule "Show Sprinklers"
when
$room : Room() $sprinkler : Sprinkler() then System.out.println( "room:" + $room.getName() + " sprinkler:" + $sprinkler.getRoom().getName() );
end
则会打印:
room:office sprinkler:office
room:office sprinkler:kitchen
room:office sprinkler:livingroom
room:office sprinkler:bedroom
room:kitchen sprinkler:office
room:kitchen sprinkler:kitchen
room:kitchen sprinkler:livingroom
room:kitchen sprinkler:bedroom
room:livingroom sprinkler:office
room:livingroom sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:livingroom sprinkler:bedroom
room:bedroom sprinkler:office
room:bedroom sprinkler:kitchen
room:bedroom sprinkler:livingroom
room:bedroom sprinkler:bedroom
那么上面这个场景在学习SQL的时候是不是也见过?就等同于select * from Room, Sprinkler
那么我们现在把上面的规则改一下:
rule "Show Sprinklers"
when
$room : Room() $sprinkler : Sprinkler( room == $room ) then System.out.println( "room:" + $room.getName() + " sprinkler:" + $sprinkler.getRoom().getName() );
end
现在打印信息如下:
room:office sprinkler:office
room:kitchen sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:bedroom sprinkler:bedroom
等同于SQL:select * from Room, Sprinkler where Room == Sprinkler.room.
有了这个对照,其实对于编写一般的Rule相信已经没有问题了。
执行流程主要分为2个阶段:
各Rule的执行顺序默认是arbitrary(任意的),但是大部分场景下我们是需要按照某些顺序执行的,因此drools给Rule提供了一些属性来解决这些问题。
即优先级,可以为正数,负数。数字越大,则优先级越高。
即把规则在agenda阶段分组,所有的Group会有栈(Stack)的方式来进行存储。 agenda-group "calculation"
例如,我们有如下的代码:
Agenda agenda = ksession.getAgenda();
agenda.getAgendaGroup( "report" ).setFocus();
agenda.getAgendaGroup( "calculation" ).setFocus();
ksession.fireAllRules();
那么calculation
会在report
之前执行,同一个group中,会按照Salience
优先级执行。
和Agenda Groups类似,不过这个是属于规则流,类似于工作流,需要额外定义rf文件。