什么是Drools
Drools是Jboss公司旗下一款开源的规则引擎,是一个基于java的规则引擎,开源的,可以将复杂多变的规则从硬编码中解放出来,
以规则脚本的形式存放在文件中,使得规则的变更不需要修正代码重启机器就可以立即在线上环境生效,有如下特点
1. 完整的实现了Rete算法;
2. 提供了强大的IDE Plugin开发支持;
3. 通过使用其中的DSL(Domain Specific
Language),可以实现用自然语言方式来描述业务规则,使得业务分析人员也可以看懂业务规则代码;
4. 提供了基于WEB的BRMS——Guvnor,Guvnor提供了规则管理的知识库,通过它可以实现规则的版本控制,及规则的在线修改与编译,使得开发人员和系统管理人员可以在线管理业务规则。
Drools 是业务逻辑集成平台,被分为4个项目:
- Drools Guvnor (BRMS/BPMS):业务规则管理系统
- Drools Expert (rule engine):规则引擎,drools的核心部分
- Drools Flow (process/workflow):工作流引擎
- Drools Fusion (cep/temporal reasoning):事件处理
工作原理

一个简单的使用demo,这里我们主要用到Drools Expert 部分。
业务场景:模拟清算对账
输入数据源:零花钱流水对账数据、快钱渠道对账数据,执行业务规则,输出平账数据、对账差异数据
零花钱流水对账数据实体类
AccountOrder.java
@Data
public class AccountOrder {
private Long puidOrderId;
private String puid;
private String fnPuid;
private Long orderId;
private Byte type;
private Date createTime;
private Byte busiType;
private int transType;
private Long transAmount;
private Long afterBalance;
private Long origPuidOrderId;
private Date expireTime;
private int orderStatus ; //0 成功
private int checkStatus;
}
快钱渠道对账数据实体类
BIll99Order.java
@Data
public class BIll99Order {
private Long outerOrderId;
private Long orderId;
private Date createTime;
private int transType;
private Long amount;
private int orderStatus ;
private int checkStatus; //0 未对平 1 对平
}
1.引入drools依赖
pom.xml
<properties>
<drools-version>6.4.0.Final</drools-version>
</properties>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-internal</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools-version}</version>
</dependency>
2.定义业务规则,可以是.drl文件 也可以是一段符合drools规则的字符串
precheck.drl
package rules;
dialect "java"
import com.shinyleo.drools.mode.AccountOrder;
import com.shinyleo.drools.mode.BIll99Order;
global java.util.List successCheckList;
//元数据定义
declare SuccessData
orderId :Long
checkresult:String
checkStatus:int
amount:Long
status:int
end
rule "precheck"
salience
when
$accountOrder : AccountOrder(checkStatus == ,$transAmount:transAmount,$orderId:orderId,$status:orderStatus)
$bill99Order : BIll99Order(checkStatus == ,outerOrderId == $orderId,amount == $transAmount,orderStatus == $status)
then
System.out.println("-----start rules-----" + $accountOrder.getOrderId());
$accountOrder.setCheckStatus(); //标记为对平
$bill99Order.setCheckStatus();
update($accountOrder);
update($bill99Order);
//获取平账数据
SuccessData successData = new SuccessData();
successData.setAmount($transAmount);
successData.setCheckresult("平账");
successData.setOrderId($orderId);
//insertLogical(successData);
insert(successData);
//返回
successCheckList.add(successData);
end
rule "successData"
when
SuccessData(checkStatus == );
then
System.out.println("success increase 1 ..." );
end
query "successList"
// 找出对平的数据
successData:AccountOrder(checkStatus == )
end
query "errorList"
// 找出还未对平的数据
errorData:AccountOrder(checkStatus == )
end
query "queryOrder" (int $status,Long $orderId)
// 找出还未对平的数据
queryOrder:AccountOrder(checkStatus == $status,orderId == $orderId)
end
3.注册规则,kmodule.xml,配置如果是多个规则,则配置如下
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/kie/6.0.0/kmodule">
<!--规则1-->
<kbase name="precheckBase" packages="rules">
<ksession name="precheck"/>
</kbase>
<!--规则2-->
<kbase name="precheckCmd" packages="rules.cmd">
<ksession name="cmd"/>
</kbase>
<!--规则3-->
<kbase name="precheckthird" packages="rules.third">
<ksession name="precheck-third"/>
</kbase>
</kmodule>
4.定义规则引擎调用接入层
DroolService.java
/**
* Created by zhugh on 17/1/12.
*/
public interface DroolService {
/**
* 根据规则文件激活规则
* @param ruleName
* @return
*/
public KieSession createKieSessionByRule(String ruleName);
/**
* 根据规则文件激活规则 并装载数据
* @param ruleName
* @return
*/
public KieSession executeKieSessionByRule(String ruleName, DataBean dataBean, Map<String,Object> globalMap);
/**
* 根据规则文件激活规则 并装载数据
* @param ruleName
* @return
*/
public ExecutionResults executeBatchCmd(String ruleName, DataBean dataBean, Map<String,Object> globalMap,String ... queryName);
/**
* 根据自定义内容创建规则
* @param ruleContent
* @return
*/
public KieSession executeKieSessionByRuleContent(String ruleContent);
/**
* 根据自定义内容创建规则,并执行规则
* @param ruleContent
* @return
*/
public KieSession executeKieSessionByRuleContent(String ruleContent,Object srcData,Object targetData,Map<String,Object> globalMap);
}
实现类
DroolServiceImpl.java
@Service("droolService")
public class DroolServiceImpl implements DroolService, InitializingBean {
Logger logger = LoggerFactory.getLogger(this.getClass());
private KieContainer kContainer;
@Override
public void afterPropertiesSet() throws Exception {
KieServices ks = KieServices.Factory.get();
this.kContainer = ks.getKieClasspathContainer();
}
@Override
public KieSession createKieSessionByRule(String ruleName) {
try{
return kContainer.newKieSession(ruleName);
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 数据载入
* @param ruleName
* @param dataBean
* @param globalMap
* @return
*/
@Override
public KieSession executeKieSessionByRule(String ruleName, DataBean dataBean, Map<String, Object> globalMap) {
KieSession kieSession = null;
try{
kieSession = kContainer.newKieSession(ruleName);
List listSrc = (List) dataBean.getSrcData();
List listTarget = (List) dataBean.getTargetData();
for(Object srcObj : listSrc){
kieSession.insert(srcObj);
}
for(Object tarObj : listTarget){
kieSession.insert(tarObj);
}
for(Map.Entry<String,Object> entry : globalMap.entrySet()){
kieSession.setGlobal(entry.getKey(),entry.getValue());
}
kieSession.fireAllRules();
}catch(Exception e){
logger.error("executeKieSessionByRule.error:{}",e);
throw new RuntimeException(e);
}finally {
if(null != kieSession){
kieSession.dispose();
}
}
return kieSession;
}
/**
* 批量数据载入
* @param ruleName
* @param dataBean
* @param globalMap
* @return
*/
@Override
public ExecutionResults executeBatchCmd(String ruleName, DataBean dataBean, Map<String, Object> globalMap,String ... queryName) {
KieSession kieSession = null;
ExecutionResults executionResults = null;
try{
kieSession = kContainer.newKieSession(ruleName);
List<Command> cmdList = new ArrayList<>();
Command srcCmd = CommandFactory.newInsertElements((Collection) dataBean.getSrcData());
cmdList.add(srcCmd);
Command tarCmd = CommandFactory.newInsertElements((Collection) dataBean.getTargetData());
cmdList.add(tarCmd);
//全局变量装载
for(Map.Entry<String,Object> entry : globalMap.entrySet()){
Command globalCmd = CommandFactory.newSetGlobal(entry.getKey(), entry.getValue(),true);
cmdList.add(globalCmd);
}
//查询装载
for(String query_Name : queryName){
cmdList.add(CommandFactory.newQuery(query_Name,query_Name));
}
Command fireCmd = CommandFactory.newFireAllRules();
cmdList.add(fireCmd);
BatchExecutionCommand command = CommandFactory.newBatchExecution(cmdList);
executionResults = kieSession.execute(command);
}catch(Exception e){
logger.error("executeBatchCmd.error:{}",e);
throw new RuntimeException(e);
}finally {
if(null != kieSession){
kieSession.dispose();
}
}
return executionResults;
}
/*
@Override
public ExecutionResults executeBatchCmd(String ruleName, DataBean dataBean, Map<String, Object> globalMap,String ... queryName) {
KieSession kieSession = null;
ExecutionResults executionResults = null;
try{
kieSession = kContainer.newKieSession(ruleName);
List<Command> list = new ArrayList<>();
Command srcCmd = CommandFactory.newInsertElements((Collection) dataBean.getSrcData());
list.add(srcCmd);
Command tarCmd = CommandFactory.newInsertElements((Collection) dataBean.getTargetData());
list.add(tarCmd);
//全局变量装载
for(Map.Entry<String,Object> entry : globalMap.entrySet()){
Command globalCmd = CommandFactory.newSetGlobal(entry.getKey(), entry.getValue(),true);
list.add(globalCmd);
}
//查询装载
for(String query_Name : queryName){
list.add(CommandFactory.newQuery(query_Name,query_Name));
}
BatchExecutionCommand command = CommandFactory.newBatchExecution(list);
executionResults = kieSession.execute(command);
}catch(Exception e){
logger.error("executeBatchCmd.error:{}",e);
throw new RuntimeException(e);
}finally {
if(null != kieSession){
kieSession.dispose();
}
}
return executionResults;
}
*/
@Override
public KieSession executeKieSessionByRuleContent(String ruleContent) {
StatefulKnowledgeSession ksession = null;
try {
KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kBuilder.add(ResourceFactory.newByteArrayResource(ruleContent.getBytes()), ResourceType.DRL);
if(kBuilder.hasErrors()){
logger.error("DroolServiceImpl.getKieSessionByRuleContent.error:{}",kBuilder.getErrors().toString());
throw new RuntimeException( kBuilder.getErrors().toString() );
}
KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
knowledgeBase.addKnowledgePackages(kBuilder.getKnowledgePackages());
ksession = knowledgeBase.newStatefulKnowledgeSession();
}catch (Exception e){
logger.error("executeKieSessionByRuleContent.error:{}",e);
throw new RuntimeException(e);
}finally {
if(null != ksession){
ksession.dispose();
}
}
return ksession;
}
@Override
public KieSession executeKieSessionByRuleContent(String ruleContent, Object srcData, Object targetData,Map<String,Object> globalMap) {
StatefulKnowledgeSession ksession = null;
try {
KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kBuilder.add(ResourceFactory.newByteArrayResource(ruleContent.getBytes()), ResourceType.DRL);
if (kBuilder.hasErrors()) {
logger.error("DroolServiceImpl.getKieSessionByRuleContent.error:{}", kBuilder.getErrors().toString());
throw new RuntimeException(kBuilder.getErrors().toString());
}
KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
knowledgeBase.addKnowledgePackages(kBuilder.getKnowledgePackages());
ksession = knowledgeBase.newStatefulKnowledgeSession();
ksession.insert(srcData);
ksession.insert(targetData);
for (Map.Entry<String, Object> entry : globalMap.entrySet()) {
ksession.setGlobal(entry.getKey(), entry.getValue());
}
ksession.fireAllRules();
}catch (Exception e){
logger.error("executeKieSessionByRuleContent.error:{}",e);
throw new RuntimeException(e);
}finally {
if(null != ksession){
ksession.dispose();
}
}
return ksession;
}
}
5.执行业务规则
@Test
public void droolsTest(){
List<AccountOrder> srcDataList = getSrcList();
List<BIll99Order> targetList = getTargetList();
Map<String ,Object> globalMap = new HashMap<>();
globalMap.put("successCheckList",new ArrayList());
DataBean<List<AccountOrder>,List<BIll99Order>> dataBean = new DataBean(srcDataList,targetList);
KieSession kieSession = droolService.executeKieSessionByRule("precheck",dataBean,globalMap);
List successlist = (List) kieSession.getGlobal("successCheckList");
System.out.println("global successlist :" + successlist.size());
//获取对平数据
QueryResults querySuccessList = kieSession.getQueryResults("successList");
System.out.println("对平数据条数:" + querySuccessList.size());
for(QueryResultsRow qr : querySuccessList){
AccountOrder order = (AccountOrder) qr.get("successData");
System.out.println("对平数据明细:" + order.getOrderId());
}
//获取未对平数据
QueryResults queryErrors = kieSession.getQueryResults("errorList");
System.out.println("未对平数据条数:" + queryErrors.size());
for(QueryResultsRow qr : queryErrors){
AccountOrder order = (AccountOrder) qr.get("errorData");
System.out.println("未对平数据明细:" + order.getOrderId());
}
//查找数据
QueryResults successData = kieSession.getQueryResults("queryOrder", new Object[]{new Integer(),L});
for(QueryResultsRow qr : successData){
AccountOrder order = (AccountOrder)qr.get("queryOrder"); //打印查询结果
System.out.println("查找单号为1000112345的对账结果结果 :" + order.getOrderId() + ":" + order.getCheckStatus());
}
kieSession.dispose();
}
//构造数据源,实际数据源从对账文件中录入
private List<AccountOrder> getSrcList(){
List<AccountOrder> srcDataList = new ArrayList<>();
AccountOrder accountOrder = new AccountOrder();
accountOrder.setOrderId(L);
accountOrder.setTransAmount(L);
accountOrder.setOrderStatus();
accountOrder.setCheckStatus();
AccountOrder accountOrder1 = new AccountOrder();
accountOrder1.setOrderId(L);
accountOrder1.setTransAmount(L);
accountOrder1.setOrderStatus();
accountOrder1.setCheckStatus();
srcDataList.add(accountOrder);
srcDataList.add(accountOrder1);
return srcDataList;
}
private List<BIll99Order> getTargetList(){
List<BIll99Order> targetList = new ArrayList<>();
BIll99Order bIll99Order = new BIll99Order();
bIll99Order.setOuterOrderId(L);
bIll99Order.setAmount(L);
bIll99Order.setOrderStatus();
bIll99Order.setCheckStatus();
BIll99Order bIll99Order1 = new BIll99Order();
bIll99Order1.setOuterOrderId(L);
bIll99Order1.setAmount(L);
bIll99Order1.setOrderStatus();
bIll99Order1.setCheckStatus();
targetList.add(bIll99Order);
targetList.add(bIll99Order1);
return targetList;
}
6.输出结果
-----start rules-----123
global successlist :1
对平数据条数:1
对平数据明细:123
未对平数据条数:1
未对平数据明细:456
7.工程结构