Drools7规则引擎教程
Drools7规则引擎教程
场景
- 风控反洗钱系统
- 商品折扣系统
- 积分系统
概述
- 把复杂冗余的业务规则与系统分离开来,做到架构的可复用、可移植性。
Drools5与Drools7版本变更
Drools是业务规则管理系统(BRMS)解决方案,设计一下项目:
- Drools Workbench:业务规则管理系统
- Drools Expert:业务规则引擎
- Drools Fusion:事件处理
- jBPM:工作流引擎
- OptalPlanner:规则引擎
Drools组成部分
- Drools规则
- Drools规则的解释执行
API划分
- 规则编译
- 规则收集
- 规则执行
Drools7依赖包
- kie-api
- drools-core
- drools-compile
Drools7 核心API
- KieServices
- KieContainer
- KieSession
一个简单的例子
- 在resources/META-INF下创建kmodule.xml
<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules">
<ksession name="all-rules"></ksession>
</kbase>
</kmodule>
- 在resource/rules下创建drools规则文件goods.drl
package rules
import com.drools.demo.model.Car
rule "test-drools7-older than 60"
agenda-group "test-drools7"
when
$car: Car(person.age > 60)
then
$car.setDiscount(80);
System.out.println("test-drools7-older than 60:" + $car.getPerson().getAge());
end
rule "test-drools-other"
agenda-group "test-drools7"
when
$car: Car(person.age < 60)
then
$car.setDiscount(60);
System.out.println("test-drools-other:" + $car.getPerson().getAge());
end
- 创建Car类
package com.drools.demo.model;
import lombok.Data;
@Data
public class Car {
private int discount = 0;
private Person person;
}
- 创建Person类
package com.drools.demo.model;
import lombok.Data;
@Data
public class Person {
private int age;
}
- 创建Drools7Test主类
package com.drools.demo.drools;
import com.drools.demo.model.Car;
import com.drools.demo.model.Person;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
public class Drools7Test {
private static KieSession getKiession() {
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("all-rules");
return kieSession;
}
/**
* 根绝agendaGroup获取该分组下的规则,用于业务规则隔离
* @param agendaGroup
* @return
*/
private static KieSession getKiesession(String agendaGroup) {
KieSession kieSession = getKiession();
//获取agendaGroup并设置焦点
kieSession.getAgenda().getAgendaGroup(agendaGroup).setFocus();
return kieSession;
}
public static void main(String... args) {
KieSession kieSession = getKiesession("test-drools7");
Person person1 = new Person();
person1.setAge(65);
Car car1 = new Car();
car1.setPerson(person1);
Person person2 = new Person();
person2.setAge(30);
Car car2 = new Car();
car2.setPerson(person2);
kieSession.insert(car1);
kieSession.insert(car2);
int count = kieSession.fireAllRules();
kieSession.dispose();
System.out.println(count);
System.out.println("car1 discount:" + car1.getDiscount());
System.out.println("car2 discount:" + car2.getDiscount());
}
}
kmodule
- kmodule默认路径放在META-INF下(见源码),drools自动解析
- kmodule中可以包含一个到多个kbase
- kbase的name要唯一
- kbase的packages为drl文件所在resources下的路径,注意区分drl文件中的package与此处的packages不一定相同。多个包用逗号隔开。默认情况下扫描resources下面所有规则文件
- kbase的default属性,标识当前KieBase是不是默认的,如果是默认的则不用名称就可以查找到该KieBase,但每个module最多有一个默认的KieBase
- kbase下面可以有一个或多个ksession,ksession的name属性必须设置,且必须唯一
kbase属性
属性值 | 默认值 | 合法的值 | 描述 |
---|---|---|---|
name | none | any | KieBase的名称,这个属性是强制的,必须设置 |
includes | none | 逗号分隔的KieBase名称列表 | 意味着本KieBase将会包含所有的include的KieBase的rule,process定义制品文件,非强制属性 |
defaule | false | true/false | 表示当前KieBase是不是默认的,如果是默认的,不用名称就可以查找该KieBase,但是每一个KieModule最多只能有一个默认的KieBase |
equalsBehavior | identity | identity,equality | 顾名思义就是等于的行为,这个equals是针对fact(事实)的,当插入一个Fact到working memory中的时候,drools引擎会检查该fact是否已经存在,如果存在的话就使用已有的FactHandle,否则就创建新的。而判断Fact是否存在的依据是通过该属性定义的方式来进行的,设置成identity,就是判断对象是否存在,可以理解为用==判断,看是否是同一个对象,如果该属性设置成equality的话,就是通过Fact对象的equals方法来判断 |
eventProcessingMode | cloud | cloud,stream | 当以云模式编译时,kieBase将事件视为正常事实,而在流模式下允许对其进行时间推理 |
ksession的属性
属性名 | 默认值 | 合法的值 | 描述 |
---|---|---|---|
name | none | any | KieSession的名称,该值必须唯一,也是强制的,必须设置 |
type | stateful | stateful, stateless | 定义该session到底是有状态(statefull)还是无状态(stateless),有状态的session可以利用working memory执行多次,而无状态的则只能执行一次 |
default | false | true,false | 定义该session是否是默认的,如果是默认的话则可以不用通过session的name来创建session,在同一个module中最多只能有一个默认的session |
clockType | realtime | realtime,pseudo | 定义时钟类型,用在事件处理上面,在复合事件处理上会用到,其中realtime表示用的是系统时钟,而pseudo则是用在单元测试时模拟用的 |
belieSystem | simple | simple,defeasible,jtms | 定义KieSession使用belief system的类型 |
KIE
- knowledge is everything
KIE的生命周期
- 编写:编写规则文件,比如:drl、BPMN2、决策表、实体类等
- 构建:构建一个可以发布部署的组件,对于KIE来说JAR文件
- 测试:部署之前对规则进行测试
- 部署:利用maven仓库将jar部署到应用程序
- 使用:程序加载jar文件,通过KieContainer对其进行解析创建KieSession
- 执行:通过KieSession对象的API与Drools引擎进行交互,执行规则
- 交互:用户通过命令行或者UI与引擎进行交互
- 管理:管理KieSession或者KieContainer对象
Fact对象
- Fact对象简单来说就是一个特殊的JavaBean
- 一般拥有getter/setter方法
- 放置于WorkingMemory当中
- 建立应用系统数据和规则的传输桥梁
Fact对象的特殊之处
- 引用传递——影响应用层的对象
- FactHandler
KieSession kieSession = getKiesession(agendaName);
Person person = new Person();
person.setAge(90);
FactHandle handle = kieSession.insert(person);
System.out.println(handle.toExternalForm());
int count = kieSession.fireAllRules();
System.out.println("Fires:" + count);
//更新working memory中对象的值,再执行
person.setAge(88);
kieSession.getAgenda().getAgendaGroup(agendaName).setFocus();
kieSession.update(handle, person);
count = kieSession.fireAllRules();
System.out.println("Fires2:" + count);
kieSession.dispose();
API
KieService
- 提供访问KIE关于构建和运行的相关对象
- 获取KieContainer,利用KieContainer来访问KBase和KSession等信息
- 获取KieRepository对象,利用KieRepository来管理KieModule等
- KieServices就是一个中心,通过它来获取的各种对象来完成规则构建、管理和执行等操作
KieContainer
- KieContainer就是一个KieBase的容器,提供获得KieBase的方法
- KieContainer的方法内部依旧通过KieBase来创建KieSession
KieBase
- 一个知识仓库,包含了若干的规则、流程、方法等
- KieBase本身并不包含运行时的数据之类的,如果需要执行规则KieBase中的规则的话,就需要根据KieBase创建KieSession
KieSession
- 基于KieBase创建,与Drools引擎打交道的会话
KieRepository
- KieRepository是一个单例对象,它是存放KieModule的仓库,KieModule由kmodule.xml文件定义
KieProject
- KieContainer通过KieProject来初始化、构造KieModule,并将KieModule存放在KieRepository中,然后KieContainer可以通过KieProject来查找KieModule定义的信息,并根据这些信息构造KieBase和KieSession
ClasspathKieProject
- ClasspathKieProject实现了KieProject接口,它提供了根据类路径中的META-INF/kmodule.xml文件构造KieModule的能力,是基于maven构造Drools组件的基本保障之一。意味着只要按照前面提到过的maven工程结构组织我们的规则文件或流程文件,只用很少的代码完成模型的加载和构建。
有状态Session和无状态Session
有状态session
- 通过KieContainer可以获取KieSession,在kmodule.xml配置文件中如果不指定ksession的type默认也是有状态的session。
- 有状态的session的特性是:我们可以通过建立一次session完成多次与规则引擎之间的交互,在没有调用dispose的一般步骤为:获取session,insert fact对象,然后调用fireAllRules进行规则匹配,随后调用dispose方法关闭session
<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules">
<ksession name="all-rules" type="stateful"></ksession>
</kbase>
</kmodule>
无状态session
- StatelessKieSession提供了一个更加便利的API,是对KieSession的封装,不再调用dispose方法进行session的关闭。
- 它隔离了每次与规则引擎的交互,不会再去维护会话的状态。
- 不再提供fireAllRules方法
- 使用场景
- 数据校验
- 运算
- 数据过滤
- 消息路由
- 任何能被描述成函数或公式的规则
一个无状态的例子
kmodule.xml
<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules" packages="rules">
<ksession name="all-rules" type="stateful"></ksession>
</kbase>
<kbase packages="stateless">
<ksession name="stateless-rules" type="stateless"></ksession>
</kbase>
</kmodule>
resources/stateless/stateless.drl
package stateless
import com.drools.demo.Person
rule "test-stateless"
when
$p:Person()
then
System.out.println($p.getAge());
end
BaseKieSession.java
package com.drools.demo.kie;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;
public class BaseKieSession {
protected KieSession getKiesessionByName(String sessionName){
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession(sessionName);
return kieSession;
}
protected KieSession getKiesession(){
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("all-rules");
return kieSession;
}
protected KieSession getKiesession(String agendaName){
KieSession kieSession = getKiesession();
kieSession.getAgenda().getAgendaGroup(agendaName).setFocus();
return kieSession;
}
protected StatelessKieSession getStatelessKiesession(){
KieServices kieServices = KieServices.get();
KieContainer kieContainer = kieServices.getKieClasspathContainer();
StatelessKieSession kieSession = kieContainer.newStatelessKieSession("stateless-rules");
return kieSession;
}
protected StatelessKieSession getStatelessKiesession(String agendaName){
StatelessKieSession kieSession = getStatelessKiesession();
// kieSession.getAgenda().getAgendaGroup(agendaName).setFocus();
return kieSession;
}
}
StatelessSessionTest.java
package com.drools.demo.test.chapter5;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.StatelessKieSession;
import java.util.ArrayList;
import java.util.List;
public class StatelessSessionTest extends BaseKieSession {
@Test
public void createTest(){
StatelessKieSession kieSession = getStatelessKiesession();
Person person = new Person();
person.setAge(35);
kieSession.execute(person);
List<Person> personList = new ArrayList<>();
personList.add(person);
kieSession.execute(personList);
}
}
规则文件
- 标准的规则文件是以“.drl”结尾的文本文件
- 可以存放用户自定义的函数、数据对象及自定义查询等
规则文件结构
- package——不需要与物理路径一致,只是用来区分不同业务,package name
- import——import static,import function
- globals——定义全局方法函数
- functions
- queries
- rule
Rule语法
- rule “name”–规则名称
- attributes–属性
- when——LHS Left Hand Side,多个条件默认用and,也可以使用or
- then——RHS Right Hand Side
- end
一个最简单的规则
rule "name"
when
then
end
属性
no-loop详解
- 定义当前的规则是否允许多次循环执行,默认是false
- 当执行update,insert,retract,modify等操作时,会循环执行规则
- 通过设置no-loop属性为true来避免死循环
package noloop
import com.drools.demo.Person
rule "test-no-loop-rule"
//no-loop false
no-loop true
when
$p:Person(age > 10)
then
$p.setAge(11);
System.out.println($p.getAge());
update($p);
end
ruleflow-group
- 在使用规则流的时候要用到ruleflow-group属性,该属性的值为一个字符串,作用是将规则划分为一个个的组,然后在规则流当中通过使用ruleflow-group属性的值,从而使用对应的规则
- 从drools6.5版本的说明文档到drools7版本的说明文档中都提到ruleflow-group和agenda-group进行合并,返回相同的底层数据结构
- 必须设置了焦点才能执行
- 设置焦点是一次性的,设置过焦点之后再次运行焦点就会失效,不会被执行
kbase.xml
<kbase name="ruleFlowGroup-rules" packages="rowflowGroup">
<ksession name="ruleFlowGroup-rules"></ksession>
</kbase>
ruleflowGroup.drl
package rowflowGroup
rule "test-rule-flow-group-1"
ruleflow-group "rule-flow-group-1"
when
then
System.out.println("rule-flow-group-1 被触发了");
end
rule "test-rule-flow-group-2"
ruleflow-group "rule-flow-group-2"
when
then
System.out.println("rule-flow-group-2 被触发了");
end
RowFlowGroupTest.java
package com.drools.demo.test;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class RowFlowGroupTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("ruleFlowGroup-rules");
//agenda-group和rowflow-group作用一样
kieSession.getAgenda().getAgendaGroup("rule-flow-group-1").setFocus();
kieSession.fireAllRules();
kieSession.getAgenda().getAgendaGroup("rule-flow-group-2").setFocus();
kieSession.fireAllRules();
kieSession.getAgenda().getAgendaGroup("rule-flow-group-1").setFocus();
kieSession.fireAllRules();
kieSession.getAgenda().getAgendaGroup("rule-flow-group-2").setFocus();
kieSession.fireAllRules();
kieSession.dispose();
}
}
lock-on-active
- 避免因FACT对象被更新使得执行过的规则被再次执行
- 触发此类规则的操作有update,retract,modify等
- 拥有no-loop的功能,同时又能避免其他规则改变FACT对象导致规则重新执行
- 设置为true,则不会触发执行
kbase.xml
<kbase name="lock-on-active" packages="lockOnActive">
<ksession name="lock-on-active"></ksession>
</kbase>
lockOnActive.drl
package lockOnActive
import com.drools.demo.Person
rule "test-lock-on-active-1"
when
$p: Person(age < 20)
then
System.out.println("test-lock-on-active-1 被触发,age=" + $p.getAge());
$p.setAge(21);
update($p)
end
rule "test-lock-on-active-2"
lock-on-active true
when
$p: Person(age >= 20)
then
System.out.println("test-lock-on-active-2 被触发,age=" + $p.getAge());
end
LockOnActiveTest.java
package com.drools.demo.test;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import java.lang.reflect.Field;
public class LockOnActiveTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("lock-on-active");
Person person = new Person();
person.setAge(19);
kieSession.insert(person);
kieSession.fireAllRules();
kieSession.dispose();
}
}
salience
- 用来设置规则执行的优先级
- salience属性的值是一个数字,数字越大优先级越高,可以是负数
- 默认是0
- 如果不设置salience,则执行是随机的
- salience可以被动态赋值
kmodule.xml
<kbase name="salience-rules" packages="salienceTest">
<ksession name="salience-rules"></ksession>
</kbase>
salience.drl
package salienceTest
import com.drools.demo.Person
rule "salience1"
salience 0
when
then
System.out.println("salience1被执行");
end
rule "salience2"
salience 1
when
then
System.out.println("salience2被执行");
end
//动态赋值,把age复制给变量sal
rule "salience3"
salience sal
when
$p: Person(sal: age)
then
System.out.println("salience3执行");
end
SalienceTest.java
package com.drools.demo.test;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class SalienceTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("salience-rules");
Person person = new Person();
person.setAge(10);
kieSession.insert(person);
kieSession.fireAllRules();
kieSession.dispose();
}
}
agenda-group
- agenda-group基本作用
- 对规则进行分组
- 设置焦点
package agendaGroup
rule "test-agenda-group-1"
agenda-group "agenda-group-test"
when
then
System.out.println("agenda-group-1被触发");
end
rule "test-agenda-group-2"
agenda-group "agenda-group-test"
when
then
System.out.println("agenda-group-2被触发");
end
rule "test-agenda-group-3"
agenda-group "agenda-group-test3"
when
then
System.out.println("agenda-group-3被触发");
end
auto-focus
- 对agenda-group和ruleflow-group的补充
- 默认false
- 设置为true,自动获取焦点
autoFocus.drl
package autoFocus
rule "test-auto-fucos-1"
agenda-group "auto-focus-1"
auto-focus false
when
then
System.out.println("auto-focus-1 被触发");
end
rule "test-auto-fucos-2"
agenda-group "auto-focus-2"
auto-focus true
when
then
System.out.println("auto-focus-2 被触发");
end
activation-group
- 该属性将若干个规则划分成一个组,统一命名默认false
- 具有相同activation-group属性的规则中只要又一个被执行,其他的规则都不再执行
- 该属性以前也被成为异或(Xor)组
package activationGroup
rule "test-activation-group=1"
activation-group "activation-group"
when
then
System.out.println("activation 1 被触发");
end
rule "test-activation-group=2"
activation-group "activation-group"
salience 1
when
then
System.out.println("activation 2 被触发");
end
date-effective
- 该属性是用来控制规则只有在到达指定时间后才会触发(生效时间)
- 与系统当前时间进行比对,大于等于系统时间会执行
- 默认date-effective的日期格式为"dd-MMM-yyyy"
- 可自定义格式
- 对照属性date-expire,用法和date-effective一样,只不过是失效时间
dateEffective.drl
package dateEffective
rule "test-date-effective-1"
//date-effective "16-Mar-2021"
date-effective "2021-03-17 21:00"
when
then
System.out.println("test-date-effective-1 被触发了");
end
rule "test-date-effective-2"
when
then
System.out.println("test-date-effective-2 被触发了");
end
DateEffectiveTest.java
package com.drools.demo.test;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class DateEffectiveTest extends BaseKieSession {
@Test
public void createTest(){
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm");
KieSession kieSession = getKiesessionByName("dateEffective-rules");
kieSession.fireAllRules();
kieSession.dispose();
}
}
定时器
- 基于interval(间隔)和cron表达式
- 间隔定时器用int来定义,它遵循java.util.Timer对象的使用方法
- Cron定时器用cron来定义,使用标准的Unix cron表达式
定时器表达式
- timer(int:?)
- timer(int:30s)
- timer(int:30s 5m)
- timer(cron:)
- timer(cron:* 0/15 * * * ?)
Server.java
package com.drools.demo;
import lombok.Data;
@Data
public class Server {
private int times;
private String result;
}
timer.drl
package timerTest
import com.drools.demo.Server
import java.util.Date
rule "test-timer"
timer (cron:* 0/1 * * * ?)
when
$s:Server(times > 5)
then
System.out.println("已经尝试" + $s.getTimes() + "次,超过预警次数");
$s.setResult(new Date() + "-服务器已经尝试" + $s.getTimes() + "次,依旧失败,发出警告");
end
TimerTest.java
package com.drools.demo.test;
import com.drools.demo.Server;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
public class TimerTest extends BaseKieSession {
@Test
public void createTest() throws Exception{
KieSession kieSession = getKiesessionByName("timerTest-rules");
Server server = new Server();
server.setTimes(0);
new Thread(()->{
//一直阻塞,直到等待到halt()
kieSession.fireUntilHalt();
}).start();
FactHandle factHandle = kieSession.insert(server);
for (int i=1;i<10;i++){
Thread.sleep(1000);
server.setTimes(i);
kieSession.update(factHandle, server);
}
Thread.sleep(3000);
kieSession.halt();
System.out.println("server尝试结果:" + server.getResult());
}
}
日历
- 日历可以单独应用于规则中,也可以和timer结合使用在规则中使用。
- 通过属性calendars来定义日历
- 可以和timer配合使用
- 适用希望在哪天触发的场景
添加quartz依赖
<dependency>
<groupId>org.opensymphony.quartz</groupId>
<artifactId>quartz</artifactId>
<version>1.6.1</version>
</dependency>
calendar.drl
package calendarTest
rule "test-calendar-rule1"
calendars "weekday"
when
then
System.out.println("test-calendar-rule1 被触发");
end
rule "test-calendar-rule2"
calendars "weekday_exclude"
when
then
System.out.println("test-calendar-rule2 被触发");
end
CalendarTest.java
package com.drools.demo.test;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import org.kie.api.time.Calendar;
import org.quartz.impl.calendar.WeeklyCalendar;
public class CalendarTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("calendarTest-rules");
kieSession.getCalendars().set("weekday", WEEKDAY);
kieSession.getCalendars().set("weekday_exclude", WEEKDAY_EXCLUDE);
kieSession.fireAllRules();
kieSession.dispose();
}
private static final Calendar WEEKDAY = new Calendar(){
@Override
public boolean isTimeIncluded(long timestamp) {
WeeklyCalendar weeklyCalendar = new WeeklyCalendar();
weeklyCalendar.setDaysExcluded(new boolean[]{false, false, false, false, false, false, false});
weeklyCalendar.setDayExcluded(java.util.Calendar.WEDNESDAY, true);//排除掉今天星期三
return weeklyCalendar.isTimeIncluded(timestamp);
}
};
private static final Calendar WEEKDAY_EXCLUDE = new Calendar(){
@Override
public boolean isTimeIncluded(long timestamp) {
WeeklyCalendar weeklyCalendar = new WeeklyCalendar();
weeklyCalendar.setDaysExcluded(new boolean[]{false, false, false, false, false, false, false});
return weeklyCalendar.isTimeIncluded(timestamp);
}
};
}
其他属性规则
- dialect:要使用的语言类型,Java,mvel
- duration:已废弃,规则将指定的时间之后在另外一个线程里触发
- enabled:设置规则是否可用,true:可用,false:不可用
dialect "java"
dialect "mvel"
LHS简介
- LHS是规则条件部分的统称,由0个或多个条件元素组成
- 如果没有条件元素那么默认就是true
- 多个跳进默认是“和”的关系
- and不具有优先绑定功能
package lhs
import com.drools.demo.model.Person
rule "test-lhs-rule"
when
// eval(true)
$p1: Person(age > 10)
// or
// and
$p2: Person(age > 20)
then
System.out.println("规则被触发");
end
KieSession kieSession = getKieSessionByName("lhs-rules");
Person person1 = new Person();
person1.setAge(50);
Person person2 = new Person();
person2.setAge(30);
kieSession.insert(person1);
kieSession.insert(person2);
kieSession.fireAllRules();
kieSession.dispose();
Pattern模式基础用法
- Pattern语法 patternBinding:patternType(“constraints”)
- 任何一个JavaBean中的属性都可以访问,不过对应的属性要提供getter方法
- 在pattern的约束条件中,可以任何返回结果为布尔类型的Java表达式
- Java表达式也可以和增强的表达式进行结合使用,比如属性访问,可以通过使用括号来更改计算优先级,如在任一逻辑或数学表达式中
- 逗号分隔符,逗号可以对约束条件进行分组,它的作用相当于“AND”
$p: Person(age % 10 == 0, name == "zhangsan")
- 变量的绑定
when
$p: Person($age: age)
then
System.out.println($age);
end
- 内部类分组访问——访问一个内部类的多个属性
$c: Car(discount < 8, person,(age == < 10, name != ""))
- 内部强制转换
//subPerson强制转换成Person
$c : Car(subPerson#Person.age == 10)
- 日期字符——规则语法中除了支持Java标准字符,同时也支持日期字符。drools默认支持的日期格式为“dd-mmm-yyyy”,可以通过设置系统变量“drools.dateformat”的值来改变默认的日期格式
$p: Person(birthday > "3-Nov-2017")
Pattern模式之List和Map
- Person(childList[0].age == 10)
- Person(credetialMap[“jsmith”].valid)
package rules
import java.util.Map
import java.util.List
import com.drools.demo.model.Person
rule "map-and-list-rule"
when
// $map: Map()
// $map: Map(this["a"] == 1)
$list: List()
$p: Person(age == 20) from $list
then
// System.out.println("a=" + $map.get("a"));
// System.out.println("b=" + $map.get("b"));
// System.out.println("person list:" + ((Person)$list.get(0)).getAge());
System.out.println("person list:" + ((Person)$p).getAge());
end
public class ListAndMapTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKieSessionByName("all-rules");
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("a", 2);
List<Person> list = new ArrayList<>();
Person person = new Person();
person.setAge(18);
list.add(person);
Person person1 = new Person();
person1.setAge(20);
list.add(person1);
kieSession.insert(map);
kieSession.insert(list);
kieSession.fireAllRules();
kieSession.dispose();
}
}
Pattern模式之运算比较符
&&和||
- Person(age > 20 && < 40)
- Person(age((>30 && <40) || (>20 && < 25)))
- Person(age > 30 && < 40 || location == “london”)
DRL特殊操作符
- < > >=
- !. 非空校验
- Person($streetName : address!.street)——address非空的时候取street赋值给streetName
- matches操作符(正则表达式)
- Cheese(type matches “(Buffalo)?\S*Mozzarella”)
- contains操作符
- memberOf——用来检查前面对象是否符合后面集合
- CheeseCounter(cheese memberOf $matureCheeses)
运算符优先级
操作类型 | 操作符 | 备注 |
---|---|---|
(嵌套/空安全)属性访问 | .!. | 非标准Java语义 |
List/Map访问 | [] | 非标准Java语义 |
约束绑定 | : | 非标准Java语义 |
乘除 | */% | |
加减 | ± | |
移位 | <<>>>>>> | |
关系 | <>>=instanceof | |
等 | ==!= | 未使用标准Java语义,某些语义相当于equals |
非短路AND | & | |
非短路异或 | ^ | |
非短路包含 | OR | |
逻辑与 | && | |
逻辑或 | || | |
三元运算符 | ?: | |
逗号分隔, 相当于and | , | 非标准Java语义 |
RHS
- RHS是满足LHS条件之后进行后续处理部分的统称
- 保持RHS的精简和可读性
- 主要功能是对working memory中的数据进行insert、update、delete或modify操作
函数
insert函数
- drl中insert函数和调用KieSession中的方法效果一样
- 调用insert之后,规则会进行重新匹配,如果没有no-loop为true或lock-on-active为true的规则,如果条件满足则会重新执行
package rules
import com.drools.demo.model.Person
rule "test-insert-rule"
when
then
System.out.println("规则被触发");
Person person = new Person();
person.setAge(27);
insert(person);
end
update函数
- update函数可对Working Memory中的Fact对象进行更新变更操作,与StatefulSession中的update的作用基本相同
- 查看KnowledgeHelper接口中的update方法可以发现,update函数由多种参数组合的使用方法
- 在实际使用中更多的会传入FACT对象来进行更新操作
update.drl
package updates
import com.drools.demo.Person
rule "update-demo-rule-1"
salience 2
when
$p: Person(age == 24);
then
System.out.println("update-demo-rule-1 规则被触发");
// $p.setAge(25);
// update($p);
end
rule "update-demo-rule-2"
salience 1
when
$p: Person(age == 25)
then
System.out.println("update-demo-rule-2 规则被触发");
System.out.println("person.age=" + $p.getAge());
end
UpdateTest.java
package com.drools.demo.test;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
public class UpdateTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("updates-rules");
Person person = new Person();
person.setAge(24);
FactHandle factHandle = kieSession.insert(person);
// kieSession.fireAllRules();
person.setAge(25);
kieSession.update(factHandle, person);
kieSession.fireAllRules();
kieSession.dispose();
}
}
delete函数
- delete函数可对working memory中的FACT对象进行删除操作,与StatefulSession中的delete的作用i本想相同
- 与retract方法效果一样,但已废弃
delete.drl
package deletes
import com.drools.demo.Person
rule "delete-demo-rule-1"
salience 2
when
$p: Person(age == 24);
then
System.out.println("update-demo-rule-1 规则被触发");
delete($p);
end
rule "delete-demo-rule-2"
salience 1
when
$p: Person(age == 24)
then
System.out.println("update-demo-rule-2 规则被触发");
end
DeleteTest.java
package com.drools.demo.test;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
public class DeleteTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("deleteDemo-rules");
Person person = new Person();
person.setAge(24);
FactHandle factHandle = kieSession.insert(person);
kieSession.delete(factHandle);
kieSession.delete(factHandle, FactHandle.State.LOGICAL);
kieSession.fireAllRules();
kieSession.dispose();
}
}
modify函数
- modify是基于结构化的更新操作,它将更新操作与设置属性相结合,用来更改Fact对象的属性
modify(<fact-expression>){
<expression>[,<expression>]*
}
modify.drl
package modifyTest
import com.drools.demo.Person
rule "modify-demo-rule-1"
salience 2
when
$p: Person(age == 21);
then
System.out.println("modify-demo-rule-1 规则被触发");
modify($p){
setAge(22)
}
end
rule "modify-demo-rule-2"
salience 1
when
$p: Person(age == 22)
then
System.out.println("modify-demo-rule-2 规则被触发");
end
ModifyTest.java
package com.drools.demo.test;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class ModifyTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("modify-rules");
Person person = new Person();
person.setAge(21);
kieSession.insert(person);
kieSession.fireAllRules();
kieSession.dispose();
}
}
结果条件
- 在Java中,如果有重复的代码我们会考虑进行重构,抽取公共方法或集成父类,以减少相同的代码在多处出现,达到代码的最优管理和不必要的麻烦
- 有条件的结果
- Drools规则的继承
- do和标记
- LHS中的if判断
condition.drl
package conditions
import com.drools.demo.Person
import com.drools.demo.Car
rule "condition-demo-rule-1"
no-loop true
when
$p: Person(age > 18)
if(age == 20) do[updatePerson1]
else do[updatePerson2]
then
System.out.println("condition-demo-rule-1 规则被触发");
then[updatePerson1]
System.out.println("执行updatePerson1");
modify($p){
setAge(17)
}
then[updatePerson2]
System.out.println("执行updatePerson2");
modify($p){
setAge(20)
}
end
rule "condition-demo-rule-2"
extends "condition-demo-rule-1"
when
// $p: Person(age > 18)
$c: Car(discount < 90)
then
System.out.println("condition-demo-rule-2 规则被触发");
end
ConditionTest.java
package com.drools.demo.test;
import com.drools.demo.Car;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class ConditionTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("condition-rules");
Person person = new Person();
person.setAge(19);
Car car = new Car();
car.setDiscount(80);
kieSession.insert(person);
kieSession.insert(car);
kieSession.fireAllRules();
kieSession.dispose();
}
}
Query查询
- Query语法提供了一种查询working memory中符合约束条件的FACT对象的简单方法
- 仅包含规则文件中的LHS部分,不用指定“when”和“then”部分
- Query有一个可选参数集合,每一个参数都有可选的类型,如果没有指定类型,则默认为Object类型
- Query名称需要全局唯一
- 使用kieSession.getQueryResults(“name”)方法可以获得查询的结果
query.drl
package querys
import com.drools.demo.Person
rule "query-demo-rule-1"
when
$p: Person(age == 20);
then
System.out.println("Name is " + $p.getName());
System.out.println("query-demo-rule-1 规则被触发");
end
query "query-by-age"
$p1 : Person(age == 23)
end
query "query-by-param" (Integer ageParam)
$p2 : Person(age >= ageParam)
end
QueryTest.java
package com.drools.demo.test;
import com.drools.demo.Person;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.QueryResults;
import org.kie.api.runtime.rule.QueryResultsRow;
public class QueryTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("query-rules");
Person person1 = new Person();
person1.setAge(20);
person1.setName("zhangsan");
Person person2 = new Person();
person2.setAge(23);
person2.setName("lisi");
kieSession.insert(person1);
kieSession.insert(person2);
kieSession.fireAllRules();
QueryResults queryResults1 = kieSession.getQueryResults("query-by-age");
System.out.println(queryResults1.size());
for (QueryResultsRow row : queryResults1) {
Person person = (Person) row.get("$p1");
System.out.println("Person1 name is: " + person.getName());
}
System.out.println("--------------------------------");
QueryResults queryResults2 = kieSession.getQueryResults("query-by-param", 20);
System.out.println(queryResults2.size());
for (QueryResultsRow row : queryResults2) {
Person person = (Person) row.get("$p2");
System.out.println("Person2 name is: " + person.getName());
}
kieSession.dispose();
}
}
Function函数
- 相当于Java中的方法,编译器会生成相应的辅助类
- 封装多处都调用的业务逻辑到一个方法中
- 函数的参数和结果数据类型与Java一致
function.drl
package functions
import com.drools.demo.Person
rule "functions-demo-rule-1"
when
then
System.out.println("functions-demo-rule-1 规则被触发");
helloFunction();
System.out.println(paramFunction("Tom"));
end
function void helloFunction(){
System.out.println("hello drools function");
}
function String paramFunction(String name){
return "Hello " + name + "!";
}
FunctionTest.java
package com.drools.demo.test;
import com.drools.demo.kie.BaseKieSession;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class FunctionTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("function-rules");
kieSession.fireAllRules();
kieSession.dispose();
}
}
Global全局变量
- global用来定义全局变量,它可以让应用程序的对象在规则文件中能够被访问
- 可以用来为规则文件提供数据或服务
- 可以用来操作规则执行结果的处理和从规则返回数据,比如执行结果的日志或值,或者与应用程序进行交互的规则的回调处理
- 全局变量并不会被插入到working memory中,因此,除非作为常量值,否则不应该将全局变量于规则约束的判断中
- 如果多个包中声明具有相同标识符的全局变量,则必须是相同的类型,并且他们都将引用相同的全局值
EmailService.java
package com.drools.demo.service;
public class EmailService {
// public static void sendEmail(){
public void sendEmail(){
System.out.println("Email has be send.");
}
}
global.drl
package globals
import com.drools.demo.Person
global com.drools.demo.service.EmailService emailService
rule "globals-demo-rule-1"
when
then
System.out.println("globals-demo-rule-1 规则被触发");
emailService.sendEmail();
end
GlobalTest.java
package com.drools.demo.test;
import com.drools.demo.kie.BaseKieSession;
import com.drools.demo.service.EmailService;
import org.junit.Test;
import org.kie.api.runtime.KieSession;
public class GlobalTest extends BaseKieSession {
@Test
public void createTest(){
KieSession kieSession = getKiesessionByName("global-rules");
EmailService emailService = new EmailService();
kieSession.setGlobal("emailService", emailService);
kieSession.fireAllRules();
kieSession.dispose();
}
}
注释
- drl文件中的注释与Java的注释方法一样
- 通过//进行注释
- 通过/*注释内容*/进行注释
异常
- drools5引入了标准化的错误信息,可以快速的查找和解决问题
- 异常分两类,一类为普通的异常,一类为规则特有的异常格式
关键字
- Drools5开始引入了硬关键字和软关键字
硬关键字
- 硬关键字是保留关键字,在命名demo对象,属性,方法,函数和规则文本中使用的其他元素时,不能使用任何硬编码关键字
- true
- false
- null
软关键字
- 软关键字只在它们的上下文中被识别,可以在其他地方使用这些词,建议避免
- 属性
- 语法关键字(package, import, when, then等)
- 条件判断等
- lock-on-active, date-effective, date-expires, no-loop, auto-focus, activation-group, agenda-group, ruleflow-group, entry-point, duration, package, import, dialect, salience, enabled, attributes, rule, extend, when, then, template, query, declare, function, global, eval, not, in, or, and, exists, forall, accumulate, collect, from, action, reverse, result, end, over, init
声明类型
- 适用于直接定义规则引擎的数据原型,不用再在Java中创建
- 新构建扩展原型
示例:
declare Address
number:int
streetName:String
city:String
end
newType.drl
package newTypes
import java.util.Date
rule "newtype-demo-rule"
when
then
Address address = new Address();
address.setCity("nanjing");
address.setNumber(111);
insert(address);
end
declare Address
number: Integer
city: String
createTime: Date
end
声明类型之继承和枚举
- 支持继承extends
- 支持枚举
package rules
import java.util.Date
rule "new_type_rule0"
when
then
Address address = new Address();
address.setName("中国");
address.setCity("北京");
address.setNumber(111);
insert(address)
end
rule "new_type_rule"
when
$address: Address(name == CountryName.CHINA.getFullName())
then
System.out.println("规则被触发");
end
declare Address extends Country
number: Integer
city: String
createTime: Date
end
declare Country
name: String
end
declare enum CountryName
CHINA("中国");
fullName: String
end
类型声明之API调用
- 在drl文件中声明的类型可以通过API的方式在代码中进行调用赋值
NewTypeTest.java
package com.drools.demo.test;
import com.drools.demo.kie.BaseKieSession;
import com.drools.demo.model.Person;
import org.junit.Test;
import org.kie.api.KieBase;
import org.kie.api.definition.type.FactType;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
public class NewTypeTest extends BaseKieSession {
@Test
public void createTest() throws Exception{
KieContainer kieContainer = getKieContainer();
KieBase kieBase = kieContainer.getKieBase("newType-rules");
FactType factType = kieBase.getFactType("com.newType", "Country");
Object country = factType.newInstance();
factType.set(country, "name", "美国");
KieSession kieSession = getKieSessionByName("newType-rules");
kieSession.fireAllRules();
kieSession.dispose();
}
}
元数据
- 描述数据属性的数据
- 元数据的作用:定义数据,描述数据等
- Drools声明元数据
package rules
import com.drools.demo.model.Person
import rules.City
rule "test-meta-rule"
when
then
City city = new City();
city = new City("北京");
city = new City("北京","中国");
System.out.println("test-meta-rule 规则被触发");
end
declare City
@author("zzz")
name: String @key
address: String
end
@author是没有实际意思的元数据
@key是有限制的元数据
决策表
初识决策表
- 决策表是一个“精确而紧凑的”表示条件逻辑的方式,非常适合商业级别的规则
- 支持xls和csv格式的文件
- 什么时候用决策表
- 规则能够被表达为指定格式模板+数据的格式,考虑使用决策表
- 很少量的规则不建议使用决策表
- 不是遵循一组规则模板的规则也不建议是使用决策表
决策表编写及编译
-
决策表格式
- RuleSet 类似package
- Squential 优先级定义
- Functions 方法函数
- 空行
- RuleTable 定义规则,类似rulename
- Condition 条件,就是LHS里的判断条件
-
决策表加载
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>7.48.0.Final</version>
</dependency>
-
使用SpreadsheetCompiler类进行编译
-
推荐使用Excel2009,xls格式
决策表实践
决策表配置
全局配置部分
- RuleSet:定义包名
- Import:指定导入的class,包括类方法
- Variables:指定全局变量
- Notes:输入任何内容
- Functions:本地方法
RuleTable部分
- CONDITION:指定单个规则的条件,条件不写的话默认就是==
- ACTION:指定rule的结果
- PRIORITY:指定rule的salience属性
- No-loop:指定rule的no-loop属性
关键字 | 说明 | 是否必须 |
---|---|---|
RuleSet | 在这个单元格内容和drl文件中的package是一样 | 必须,只能有一个(如果为空,则使用默认值) |
Sequential | 右边的单元可以是true或False,如果是true则确保规则按照从表格的上面到下面的顺序执行(规则是从上到下,如果是false就是乱序) | 可选 |
Import | 要导入规则库中的类的列表 | 可选 |
Functions | 紧接右边的单元格可以包含函数,其可用于规则的片段中。drools支持在drl中定义函数,允许逻辑被嵌入在规则中,不用硬编码改变,小心使用,语法与标准DRL相同 | 可选 |
Variables | 紧跟在右边的单元格包含global声明,格式是类型跟着变量名与DRL中的global一个意思 | 可选 |
Queries | 紧接右边的单元格可以包含Drools支持的全局声明,它一个类型,紧跟着一个查询名字与drl中的query是一个意思 | 可选 |
RuleTable | 这个的意思是表示规则名,写法是在RuleTable后直接写规则名,不用另写一列 | 必选 |
CONDITION | 指明该列被用于规则条件CONDITION相当于drl中的when | 每个规则表至少一个 |
ACTION | 指明该列将被用于推论,简单理解为结果,相当于drl中r then ACTION 与CONDITION是平行的 | 每个规则表至少一个 |
PRIORITY | 指明该列的值将被设置为该规则行的salience值 | 可选 |
DURATION | 指明该列的值将被设置为该规则的期限duration | 可选 |
NAME | 指明该列的值将被设置为从那行产生的规则名字 | 可选 |
NO-LOOP | 指明这个规则不允许循环 | 可选 |
ACTIVATION-GROUP | 在这个列中的单元格的值,指出该规则行属于特定的XOR活动组 | 可选 |
AGENDA-GROUP | 在这个列中的单元格的值,指出该规则行属于特定的议程组,可以理解成获取焦点 | 可选 |
RULEFLOW-GROUP | 在这个列中的单元格的值,指出该规则行属于特定的规则流组 | 可选 |
Drools与Springboot集成
依赖包
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring.version>2.3.2.RELEASE</spring.version>
<drools.version>7.0.0.Final</drools.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<type>pom</type>
<version>${spring.version}</version>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>mysql</groupId>-->
<!-- <artifactId>mysql-connector-java</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${drools.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-actuator</artifactId>-->
<!-- <version>1.2.3.RELEASE</version>-->
<!-- </dependency>-->
</dependencies>
动态加载规则
- 第一种:KieHelper的reload方式,reloadByHelper
- 第二种:KieFileSystem重新加载,reload
DroolsAutoConfiguration.java
package com.demo.drools.config;
import com.demo.drools.utils.KieUtils;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
@Configuration
public class DroolsAutoConfiguration {
private static final String RULES_PATH = "rules/";
@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws Exception{
KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
for (Resource file: getRuleFiles()){
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
}
return kieFileSystem;
}
@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws Exception{
KieServices kieServices = getKieServices();
final KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(new KieModule() {
@Override
public ReleaseId getReleaseId() {
return kieRepository.getDefaultReleaseId();
}
});
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
Results results = kieBuilder.getResults();
if (results.hasMessages(Message.Level.ERROR)){
System.out.println(results.getMessages());
throw new IllegalStateException("### errors ###");
}
kieBuilder.buildAll();
KieContainer kieContainer = kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
KieUtils.setKieContainer(kieContainer);
return kieContainer;
}
@Bean
@ConditionalOnMissingBean(KieBase.class)
public KieBase kieBase() throws Exception{
return kieContainer().getKieBase();
}
@Bean
@ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
public KModuleBeanFactoryPostProcessor kiePostProcessor(){
return new KModuleBeanFactoryPostProcessor();
}
private Resource[] getRuleFiles() throws Exception{
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
}
private KieServices getKieServices(){
return KieServices.Factory.get();
}
}
ReloadDroolsRules.java
package com.demo.drools.component;
import com.demo.drools.utils.KieUtils;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.utils.KieHelper;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
@Component
public class ReloadDroolsRules {
private String loadRules(){
//从数据库加载规则
return "package plausibcheck.adress\n\n rule \"Postcode 6 numbers\"\n\n when\n then\n System.out.println(\"规则2中打印日志:校验通过!\");\n end";
}
private KieServices getKieServices(){
return KieServices.Factory.get();
}
public void reload() throws UnsupportedEncodingException {
KieServices kieServices = getKieServices();
KieFileSystem kfs = kieServices.newKieFileSystem();
kfs.write("src/main/resources/rules/temp.drl", loadRules());
KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
Results results = kieBuilder.getResults();
if (results.hasMessages(Message.Level.ERROR)){
System.out.println(results.getMessages());
throw new IllegalStateException("### errors ###");
}
KieUtils.setKieContainer(kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId()));
System.out.println("reload新规则重载成功");
}
public void reloadByHelper() throws UnsupportedEncodingException{
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(loadRules(), ResourceType.DRL);
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.ERROR)){
throw new IllegalStateException("### errors ###");
}
KieContainer kieContainer = kieHelper.getKieContainer();
KieUtils.setKieContainer(kieContainer);
System.out.println("新规则重载成功");
}
}
KieUtils.java
package com.demo.drools.utils;
import org.kie.api.runtime.KieContainer;
public class KieUtils {
private static KieContainer kieContainer;
public static KieContainer getKieContainer(){
return kieContainer;
}
public static void setKieContainer(KieContainer kieContainer){
KieUtils.kieContainer = kieContainer;
}
}
TestController.java
package com.demo.drools.controller;
import com.demo.drools.component.ReloadDroolsRules;
import com.demo.drools.model.Address;
import com.demo.drools.model.fact.AddressCheckResult;
import com.demo.drools.utils.KieUtils;
import org.kie.api.runtime.KieSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.IOException;
@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private ReloadDroolsRules reloadDroolsRules;
@ResponseBody
@RequestMapping("/address")
public void test(){
KieSession kieSession = KieUtils.getKieContainer().newKieSession();
Address address = new Address();
address.setPostcode("994251");
AddressCheckResult result = new AddressCheckResult();
kieSession.insert(address);
kieSession.insert(result);
int ruleFiredCount = kieSession.fireAllRules();
System.out.println("触发了" + ruleFiredCount + "条规则");
if (result.isPostCodeResult()){
System.out.println("规则校验通过");
}
kieSession.dispose();
}
@ResponseBody
@RequestMapping("/reload")
public String reload() throws IOException{
reloadDroolsRules.reload();
return "OK";
}
}