Drools(BRMS) 速成教程(上)
大家在日常开发中,肯定遇到过一些业务规则变来变去的需求,比如:会员积分系统(今天要新注册会员送10积分,明天要改成注册送优惠券,后天搞活动要改成注册自动变成高级会员...),此类需求,一般都是通过写if分支来实现的,参考下面:
if (规则条件1){
//处理1
}
else if (规则条件2){
//处理2
}
else if (规则条件3){
//处理3
}
...
这种代码毫无营养,而且很枯燥,有没有办法,将业务规则从代码中抽离出来,以后规则变了,不用改代码,只改规则配置就行?
今天要介绍的Drools,可以很好的解决此类问题,Drools是一个业务规则管理的开源框架,现在归到jboss旗下,本文将介绍一些基本的用法,方便大家快速上手。
一、添加依赖项
<properties>
<drools.version>6.5.0.Final</drools.version>
<lombok.version>1.18.2</lombok.version>
</properties> <dependencies>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version> </dependency>
</dependencies>
注:不同版本的drools,api有较大差异,本文采用6.5.0.Final版本,其它版本的用法请自行参考官方文档。(lombok是可选的,建议加上,简化java代码书写)
二、新建一个演示用的pojo类Message
package com.cnblogs.yjmyzz.drools.demo.model; import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter; @Setter
@Getter
@AllArgsConstructor
public class Message { public enum MessageType {
HI,
GOODBYE,
CHAT
} private MessageType messageType;
private String target; }
很简单,不用多说,我们要模拟的场景是针对不同的messageType及target(也就是不同的业务规则 ),代码能做出不同的处理。
三、编写业务规则drl文件
drl 是drools rule的缩写,大概长这个样子:(规则文件一般放在resources资源目录或下面的子目录中),将下面的内容保存在hello.drl中
package com.cnblogs.yjmyzz.drools; import com.cnblogs.yjmyzz.drools.demo.model.Message;
import java.util.concurrent.atomic.AtomicInteger; global String temp;
global AtomicInteger count; //函数示例
function void print(String messgae){
System.out.println(messgae);
} //规则1
rule "say-hi"
when
$message: Message(Message.MessageType.HI.equals(messageType) && target!=null)
then
print("hi," + $message.getTarget() + ", welcome to drools\n");
end
这里面可以分成几个部分:
3.1 package部分
这个是用来管理包的,跟java的package概念类似,多个rule文件时,可以按包来管理rule代码。
3.2 import
drl 规则文件中,可以直接使用java定义好的类,只需要import进来就好。
3.3 global
相当于全局变量声明,多个规则文件中可共享该变量(后面会演示这一用法),要注意的是:共享全局变量建议不要用Integer这种"简单"类型,这样无法在规则文件中修改变量的"值",建议用复杂类型(比如上面的AtomicInteger)
3.4 function
即:函数,可以定义一些共用函数,在本drl文件被其它规则共用。
3.5 rule ... when ... then ... end
这个就是真正的规则了,rule后面的"say-hi"为规则名称,when后面的相当于判断条件(注:声明条件的同时,还能声明所谓fact"变量"-[不太准确,暂且这样叫吧],$message: Message(...) 这里就相当于把后面一串东西,保存在$message这个fact"变量中)
小结一下:上面这个规则,相当于,如果Message的实例,其messageType为HI,且target值不为空,就打印输出一句话。
很简单吧,我们再加点难度,多加几个规则 :
package com.cnblogs.yjmyzz.drools; import com.cnblogs.yjmyzz.drools.demo.model.Message;
import java.util.concurrent.atomic.AtomicInteger; global String temp;
global AtomicInteger count; //函数示例
function void print(String messgae){
System.out.println(messgae);
} //规则1
rule "say-hi"
when
$message: Message(Message.MessageType.HI.equals(messageType) && target!=null)
then
print("hi," + $message.getTarget() + ", welcome to drools\n");
end rule "say-goodbye"
when
$message: Message(Message.MessageType.GOODBYE.equals(messageType) && target!=null)
then
print("bye bye ," + $message.getTarget() + "\n");
end rule "chat-and-goodbye"
when
$message: Message(Message.MessageType.CHAT.equals(messageType) && target!=null)
then
print($message.getTarget() + ", nice to meet you. But I have to go.");
//将MessageType设置成GOODBYE
$message.setMessageType(Message.MessageType.GOODBYE);
//更新fact,以便触发规则"say-goodbye"
update($message);
end rule "give-me-money"
salience -1 //规则触发的优先级,值越大,越先触发
when
$message: Message(target.equals("beggar"))
then
print("5毛拿好");
end rule "give-me-rice"
salience 1
when
$message: Message(target.equals("beggar"))
then
print("给你个包子吧");
end //本规则的效果:如果target="loop",会循环触发,真到10次后停下
rule "loop"
// no-loop //加上这行后,将禁止循环触发
when
$message: Message(target.equals("loop") && count.get()<10)
then
print("\n我会每隔1秒触发,10次后停止!" + count.addAndGet(1));
Thread.sleep(1000);
update($message)
end
解释下:
a: "chat-and-goodbye" 这条规则,如果messageType=CHAT,会修改$message.messageType为GOODBYE,然后update($mesage),相当于修改了Message实例后,会重新匹配say-goodbye规则
b:"give-me-money"、"give-me-rice" 这二个规则设置了salience,其实就是优先级,值越大,该规则越优先匹配。
c: "loop" 最后一条规则,这里用到了一个全局变量count,每次该规则且匹配到以后,计数器+1,然后再update,又匹配到本条规则,最终规则就是循环触发10次。
一个项目里,可以同时有多个规则文件,还可以再加一个hello2.drl,演示共享变量
package com.cnblogs.yjmyzz.drools; import com.cnblogs.yjmyzz.drools.demo.model.Message; rule "global-demo"
salience -99
when
$message: Message(target.equals("beggar"))
then
System.out.println(temp);
end
这里打印了共享变量temp(前提是target="begger")
四、resource/META-INF里放置kmodule.xml文件
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="hello" packages="hello">
<ksession name="ksession-hello"/>
</kbase>
</kmodule>
这个文件的主要作用之一,是在运行时,让drools知道加载哪些drl文件。注意:这里packages="hello",就表示加载classpath:resources/hello下的drl文件。
最后项目的文件结构类似这样:
五、跑一把
HelloApp内容如下:
package com.cnblogs.yjmyzz.drools.demo; import com.cnblogs.yjmyzz.drools.demo.model.Message;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession; import java.util.concurrent.atomic.AtomicInteger; public class HelloApp { public static void main(String[] args) {
KieContainer kContainer = null;
try {
KieServices ks = KieServices.Factory.get();
kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("ksession-hello"); Message message1 = new Message(Message.MessageType.HI, "杨过");
kSession.insert(message1);
kSession.fireAllRules(); Message message2 = new Message(Message.MessageType.GOODBYE, "姑姑");
kSession.insert(message2);
kSession.fireAllRules(); Message message3 = new Message(Message.MessageType.CHAT, "美羊羊");
kSession.insert(message3);
kSession.fireAllRules(); Message message4 = new Message(null, "beggar");
kSession.setGlobal("temp", "我是谁?我在哪?我要干什么?");
kSession.insert(message4);
kSession.fireAllRules(); Message message5 = new Message(null, "loop");
kSession.setGlobal("count", new AtomicInteger(0));
kSession.insert(message5);
kSession.fireAllRules(); } catch (Exception e) {
e.printStackTrace();
} finally {
if (kContainer != null) {
kContainer.dispose();
}
} }
}
注意下共享变量,即:message4,message5部分,一般是在规则触发前提前把共享变量先设置好初始值,最终输出如下:
hi,杨过, welcome to drools //规则:say-hi bye bye ,姑姑 //规则:say-goodbye 美羊羊, nice to meet you. But I have to go. //规则:chat-and-goodbye
bye bye ,美羊羊 //规则: say-goodbye(2次匹配成功) 给你个包子吧 //规则:give-me-rice
5毛拿好 //规则:give-me-money
我是谁?我在哪?我要干什么?//hello2.drl中的规则"global-demo" 我会每隔1秒触发,10次后停止!1 //规则:loop循环10次 我会每隔1秒触发,10次后停止!2 我会每隔1秒触发,10次后停止!3 我会每隔1秒触发,10次后停止!4 我会每隔1秒触发,10次后停止!5 我会每隔1秒触发,10次后停止!6 我会每隔1秒触发,10次后停止!7 我会每隔1秒触发,10次后停止!8 我会每隔1秒触发,10次后停止!9 我会每隔1秒触发,10次后停止!10
参考文章:
小明历险记:规则引擎drools教程一
drools 6.5.0官方文档
Learn Drools: Part I
Learn Drools: Part II (Cross Product)
Learn Drools: Part III (Filter Facts)
Learn Drools (Part 4): Inferences
Learn Drools (Part 5): Truth Maintenance
Learn Drools (Part 6): Rules and Statistics
Learn Drools (Part 7): Salience
Drools语法篇之Global全局变量
《Drools7.0.0.Final规则引擎教程》第4章 global全局变量
附:文件示例代码drools-helloworld可从github下载
Drools(BRMS) 速成教程(上)的更多相关文章
- Mysql DBA 20天速成教程,DBA大纲
Mysql DBA 20天速成教程 基本知识1.mysql的编译安装2.mysql 第3方存储引擎安装配置方法3.mysql 主流存储引擎(MyISAM/innodb/MEMORY)的特点4.字符串编 ...
- Mysql DBA 20天速成教程
Mysql DBA 20天速成教程 基本知识1.mysql的编译安装2.mysql 第3方存储引擎安装配置方法3.mysql 主流存储引擎(MyISAM/innodb/MEMORY)的特点4.字符串编 ...
- 极·Java速成教程 - (1)
序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...
- 极*Java速成教程 - (1)
序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...
- 七牛云存储Python SDK使用教程 - 上传策略详解
文 七牛云存储Python SDK使用教程 - 上传策略详解 七牛云存储 python-sdk 七牛云存储教程 jemygraw 2015年01月04日发布 推荐 1 推荐 收藏 2 收藏,2.7k ...
- 魔兽争霸RPG地图开发速成教程
魔兽争霸RPG地图开发速成教程 1 打开WE编辑器 下载地址 http://rpg.dz.blizzard.cn/authors-home/editor-download 然后新建地图 2 打开工 ...
- PyQT5速成教程-4 Qt Designer实战[上]
本文由 沈庆阳 所有,转载请与作者取得联系! 前言 在前面几节的学习中,我们对PyQt的基本使用.Qt Designer与Python编码的工作流程有了基本的学习.同时也掌握了Qt Designer中 ...
- EditText使用详解-包含很多教程上看不到的功能演示
写道 标题有点大,说是详解,其实就是对EditText的一些常用功能的介绍,包括密码框,电话框,空白提示文字等等的讲解,尽量的介绍详细一点,也就是所谓的详解了..呵呵 广告一下我的应用“我团”,最新1 ...
- GitHub教程--上传项目四步法 GitBash命令行下使用方法
之前就用过GitHub,感觉用GitHub托管自己的代码非常不错.可是之前用的都是窗口化的TortoiseGit,省了很多命令行的操作,但是个人非常喜欢使用命令行,于是,今天就试着用了用GitBash ...
随机推荐
- asp.net core 自定义401和异常显示内容(JWT认证、Cookie Base认证失败显示内容)
asp.net core 2.0使用JWT认证园子里已经有挺多帖子了,但开发中发现认证未授权情况下返回的401状态码是没有任何信息的,业务中可能有需要返回一串错误的Json信息.在这里我分享一个自定义 ...
- 【BZOJ】3022: [Balkan2012]The Best Teams
原题链接 题面 (为啥这题没有题面-- 给出\(N\)个人,和年龄\(age_{i},skill_{i}\) 然后给出\(M\)个询问,就是年龄在\(a\)以下选不超过\(k\)个人 要求选择的人水平 ...
- linux重启服务的脚本命令
最近做网站测试,每次测试完成都要重启服务,为此写了一个简单的shell脚本 linux服务重启shell脚本示例 2014年12月18日 linux服务重启脚本,如何实现linux服务的定时重启,可以 ...
- css3实现旋转表
如图所示: css部分: <style> #clock{width:100px; height:100px; border-radius:50%; border:4px solid bla ...
- BZOJ4003 [JLOI2015]城池攻占 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4003 题意概括 题意有点复杂,直接放原题了. 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑 ...
- Python交互图表可视化Bokeh:2. 辅助参数
图表辅助参数设置 辅助标注.注释.矢量箭头 参考官方文档:https://bokeh.pydata.org/en/latest/docs/user_guide/annotations.html#col ...
- 2017Nowcoder Girl D - 打车
题目描述 妞妞参加完Google Girl Hackathon之后,打车回到了牛家庄. 妞妞需要支付给出租车司机车费s元.妞妞身上一共有n个硬币,第i个硬币价值为p[i]元. 妞妞想选择尽量多的硬币, ...
- Java常用API及Math类
一.API的概述 API——Application Programing Interface:应用程序编程接口,是java提供的一些预定义的函数: 目的:基于API实现程序的快速编写,只需了解其作用, ...
- 解决win10 64位 + Python3 安装PyAutoIt报不是有效win32应用程序的办法
在win10 64位 + Python3的环境中,安装pyautoit第三方包的时候,报错提示为:不是有效win32应用程序.因为这个问题,进了python群,去寻求答案,却告知为,换32位的电脑,这 ...
- 上海市2019年公务员录用考试第一轮首批面试名单(B类)
上海市2019年公务员录用考试第一轮首批面试名单(B类) 2019-03-12 设置字体:大 中 小 职位序号 注册编号 职位序号 注册编号 职位序号 注册编号 职位序号 注册编号 1910565 5 ...