Drools规则引擎实践直⽩总结
⽬录
Drools规则引擎,⽹上⼤把相关的⽂章介绍,但我感觉不够直⽩,理解有些困难,且知识点没有集中⽐较分散、有些还是早前版本的内容,对与新⼿来说上⼿可能⽐较慢,⽽且
⽐较容易⾛弯路,故我在深⼊研究并实践于项⽬中后,在空闲时间花费精⼒整理了这篇⽂章,分享出来,便⼤家快速上⼿。
1. 创建Drools环境(引⼊Drools相关依赖包、现在都流⾏spring boot,故最简单有效的依赖才是最好的,kie-spring内部⾃⾏依赖了drools相关核⼼的依赖包)
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.55.0.Final</version>
</dependency>
2. 了解Drools语法及其含义(LHS、RHS、Fact)
1. DRL⽂件基本格式:
stwrod //包名,必需,这是逻辑上,与物理路径⽆关
import xxxxx; //可选,导⼊要使⽤的类名(还⽀持直接导⼊静态⽅法)
global java.util.List myGlobalList;//可选,定义全局变量(该变量由外部tGlobal传⼊)
function getResult(...){ //可选,⾃定义函数
}
query "query_gt_0"(...) //可选,⾃定义查询(仅只有LHS内容)
$result:规则Pattern
end
rule “test001” //规则名称,必需,且需唯⼀
when //规则开始关键字,必需
//这⾥如果为空则表⽰ eval(true); LHS内容(即:规则条件)
then //规则条件结束关键字,必需,后⾯部份则是RHS内容(即:规则触发的逻辑)
System.out.println(“hello drools!”);
end //规则结束关键字
2. 涉及的名词解释:
1. LHS:条件部分⼜被称之为 Left Hand Side,简称为 LHS,在⼀个规则当中 when 与 then 中
间的部分就是 LHS 部分。在 LHS 当中,可以包含 0~n 个条件,如果 LHS 部分没空的话,
那么引擎会⾃动添加⼀个 eval(true)的条件,由于该条件总是返回 true,所以 LHS 为空的规
则总是返回 true。LHS涉及的匹配元素⽤法如下:
Pattern 模式,语法:事实类型(约束),其中约束是可选的,如:Person(age>18),意思是:匹配⼯作内存中是Person类型且age>18,若存在则为true,即命中
该条规则;Pattern 模式⽀持多个,之间使⽤空格或换⾏即可;【通俗点:当前⼯作内存中有没有这个类型的对象(fact)】
字段约束,即Pattern 模式中括号中的部份,⼀般有:单值限制(如:age>18)、复合值限制(Person(x in (0,1)),注:暂⽀持in与not in)和多限制(如:
age>18 && age<30 或 age ( (> 30 && < 40) || (> 20 && < 25) )) 3种限制模式;字段约束之间⽀持:||、&&、and、or、,(逗号即为AND)【通俗点:当前⼯作内
中的这个类型(fact)的对象属性还需满⾜相关的约束条件】
条件元素 eval,条件元素 eval 本实上是包罗万象的,它允许执⾏任何语义代码(返回⼀个 boolean 原型)【通俗点:动态解释执⾏代码逻辑,与js的eval有类
似功能】
条件元素 not,⽤于检查在⼯作内存中不存在某东西。把"not"看作“⼀定没有……”的意思
条件元素 exists,⽤于检查在⼯作内存中存在某类型(fact)。把"exists"看作“⾄少有⼀个……”的意思。(如果匹配到多个事实fact对象,也只会触发执⾏⼀次
RHS中逻辑)
条件元素 forall,⽤于检查在⼯作内存中所有的对象(fact)都必需满⾜Pattern 模式,若有1个不满⾜,则为fal,只有全部满⾜才为true;(如果匹配到多个事
反对铺张浪费实fact对象,也只会触发执⾏⼀次RHS中逻辑)
条件元素 from,让⽤户指定任意的资源,⽤于 LHS 模式的数据匹配。这允许引擎在⾮⼯作内存数据的基础上进⾏推断。数据源可以是⼀个绑定变量的⼀个⼦字
段,或者⽅法调⽤的结果。它是⼀个超强结构,允许开箱即可与其他应⽤程序组件或框架集成使⽤【通俗点:from后⾯是指定⼀个⾃定义的数据源,from前⾯是
from后⾯结果得到的,类似sql中的lect field=value from table;】
条件元素 collect,允许规则在来⾃特定资源或⼯作内存的⼀个对象集合上进⾏推断【通俗点:就是将符合匹配到多个事实fact对象累加到⼀起形成⼀个
Collection集合】
条件元素 accumulate,是⼀个更灵活强⼤的 collect 形式,它主要做的事是允许规则迭代整个
⼀个对象的集合,为每个元素定制执⾏动作,并在结束时返回⼀个结果对象, accumulate
既⽀持预定义的累积函数的使⽤,或也可以使⽤内联的⾃定义代码,简化的语法如下:
accumulate( < source pattern 源模式 >; < functions 函数 > [;< constraints >] ),其中函数除了内置的还可以⾃定义JAVA函数,只需使⽤import accumulate 类型
(该类型需实现AccumulateFunction接⼝)⾃定义⽅法名;
⽰例代码:
accumulate(Message(createBy=="zuowj",$id:id);$countNum:count($id);$countNum>1)
//含义:查找⼯作内存中有Message类型的且过滤条件为(createBy=="zuowj")fact事实对象,并取出id,然后对所有的id进⾏count,最后判断count的结果是否>1,转换为SQL理解就是:
//lect id from Message where createBy='zuowj' group by id having count(id)>1;这样应该好理解吧!
inline 的语法结构:
< result pattern >from accumulate(< source pattern >,init(< init code >),action(< action
code >),rever(< rever code >),result(< result expression >) )
< source pattern >:这个表⽰源模式。⽤法:也就是我们常⽤⼿ Object(xx:XX 属性) 这个会去匹配每⼀个源对象;
< init code >:⽤法说明:init 是做初始化⽤的,简单的说,在 source pattern 遍历完之后就已经触发,有点像 for 的开头;
< action code >: ⽤法说明:action 会执⾏所以满⾜条件的源对象进⾏操作,像是 for的⽅法体。在⾥⾯可写 java code;
< rever code >: 这是⼀个可选的被选⽅⾔的语义代码块,如果存在,将为不再匹配资源模式的每个资源对象执⾏。这个代码块的⽬的是不做在< action code >
块中做的任何计算,所以,当⼀个资源对象被修改或删除收时,引擎可能做递减计算,极⼤地提升了这些操作的性能;
< result expression >: 返回值,是根据 action 上⾯两个遍历出来的结果进⾏⼀个返
回,这个返回值中也可以进⾏计算。
弹无虚发的意思
< result pattern >: 返回值类型,在< result expression >返回值的类型再⼀次进⾏匹
配,如果匹配不成功则返回 fal。
⽰例代码:
$res:String() from accumulate(Message(createBy=="zuowj",$cont:content),init(String allContent="";),action(allContent +=$cont;),result(allContent))
//含义:for循环遍历⼯作内存中Message类型且过滤条件为(createBy=="zuowj")的fact对象,初始化设置allContent="",每次执⾏allContent +=$cont,遍历完成后将allContent返回给#res变更接收,类似JAVA for代码如下:
// String res="",allContent="";
//for (Object o:List<Object>){
/
/ if(o instanceof Message && ((Message)o).getContent()=="zuowj"){
// allContent+=((Message)o).getContent();
// }
//}
//res=allContent;
2. RHS:结果部分⼜被称之为 Right Hand Side,简称为 RHS,在⼀个规则当中 then 后⾯部分就是 RHS,只有在 LHS 的所有条件都满⾜时 RHS 部分才会执⾏。RHS
部分是规则真正要做事情的部分,可以将因条件满⾜⽽要触发的动作写在该部分当中,在 RHS 当中可以使⽤ LHS 部分当中定义的绑定变量名、设置的全局变量、或者是直接编写 Java 代码(对于要⽤到的 Java 类,需要在规则⽂件当中⽤ import 将类导⼊后⽅能使⽤,这点和 Java ⽂件的编写规则相同,且不建议在RHS中写条件判断,如果需要条件判断,那么请重新考虑将其放在 LHS 当中,否则就违背了使⽤规则的初衷。),同时在 RHS ⾥⾯,还提供了⼀些对当前 Working Memory 实现快速操作的宏函数或对象,⽐如 inrt/inrtLogical、update/modify 和 retract 就可以实现对当前 WorkingMemory 中的 Fact 对象进⾏新增、修改或者是删除;如果您觉得还要使⽤ Drools 当中提供的
其它⽅法,那么您还可以使⽤另⼀外宏对象 drools,通过该对象可以使⽤更多的操作当前 Working Memory 的⽅法;同时 Drools 还提供了⼀个名为 kcontext 的宏对象,使我们可以通过该对象直接访问当前 Working Memory 的 KnowledgeRuntime。另外,通过注册Channel实现命中规则后通过channels[通道名].nd发送消息,并传递给JAVA代码的订阅回调⽅法;
3. function:函数类似JAVA中的有返回值的⽅法,将RHS中涉及⼀些重复的动作封装定义成函数(⽀持定义⼊参),能够有效的简少重复逻辑的编写,但注意,函数的
应⽤是不建议直接写在LHS块中的,若需使⽤需使⽤eval关键字,类似:eval(hello("梦在旅途"));
4. query:查询是⼀种搜索⼯作内存中与指定条件匹配的事实的简单⽅法。因此,它只包含规则的
LHS 的结构,因此既不指定“when”也不指定“then”。查询具有可选的参数集,每个参数
可以可选地键⼊。如果未给出类型,则假定类型为 Object。引擎将根据需要尝试强制值。
查询名称对于 KieBa 是全局的;因此不要向同⼀ RuleBa 的不同包添加相同名称的查询。
要返回结果,请使⽤ QueryResults(“name”),其中“name”是查询
的名称。这将返回查询结果的列表,这允许您检索与查询匹配的对象。查询以 query 关键字开始,以 end 关键字结束,在 package 当中⼀个查询要有唯⼀的名称,查询的内容就是查询的条件部分,条件部分内容的写法与规则的 LHS 部分写法⾮常相同。
5. global:全局变量(类似java中的final static定义的变量),同⼀个ssion中所有rule都共享使⽤(如果多个包使⽤相同的标识符声明了全局变量,那么它们必须有相
同的类型,并且它们所有都会引⽤相同的全局变量的值),全局变量没有被插⼊到⼯作内存,因此,全局变量绝不能被⽤来在规则中建⽴条件,除⾮它是⼀个恒定不变的值。引擎不能知道全局变量的改变,不能跟踪它们的变化。还需注意:常量值是不能改变的、包装类是不能改变的、类似 javaBean,List 这类的操作,是可以改变内容的,但内存地址不会变;
3. Drools的属性说明(⼀般在在rule 名称与when之前设置属性):
1. Salience 优先级,作⽤是⽤来设置规则执⾏的优先级, salience 属性的值是⼀个数字,数字越⼤执⾏优先级越⾼,同时它的值可以是⼀个负数。默认情况下,规则
的 salience 默认值为 0;
2. no-loop 防⽌死循环,作⽤是⽤来控制已经执⾏过的规则在条件再次满⾜时是否再次执⾏,no-loop
属性的值是⼀个布尔型,默认情况下规则的no-loop 属性的值为
fal,如果 no-loop 属性值为true,那么就表⽰该规则只会被引擎检查⼀次,如果满⾜条件就执⾏规则的 RHS 部分;
3. date- effective ⽇期⽐较⼩于等于,该属性是⽤来控制规则只有在到达后才会触发,在规则运⾏时,引擎会⾃动拿当前操作系统的时候与 date-effective 设置的时间值进
⾏⽐对,只有当前系统时间>=date-effective 设置的时间值时,规则才会触发执⾏,否则执⾏将不执⾏;
4. date- exspires ⽇期⽐较⼤于,该属性的作⽤与 date-effective 属性恰恰相反,当前系统时间<date-expires 值,date-expires 的作⽤是⽤来设置规则的有效期,引擎在
执⾏规则的时候,会检查规则有没有 date-expires 属性,如果有的话,那么会将这个属性的值与当前系统时间进⾏⽐对,如果⼤于系统时间,那么规则就执⾏,否则就不执⾏;
5. Dialect ⽅⾔,该属性⽤来定义规则当中要使⽤的语⾔类型,⽀持 mvel 和 java,默认是java;
6. Enabled 是否可⽤,⽤来定义⼀个规则是否可⽤的,如是设为fal则不会执⾏该规则,默认为true;
7. lock- on-active 规则只执⾏⼀次,当在规则上使⽤ ruleflow-group属性或 agenda-group 属性的时候,将 lock-on-action属性的值设置为 true,可能避免因某些 Fact 对
象被修改⽽使已经执⾏过的规则再次被激活执⾏;
8. activation-group 分组,作⽤是将若⼲个规则划分成⼀个组,⽤⼀个字符串来给这个组命名,这样在执⾏的时候,具有相同 activation- - group 属性的规则中只要有⼀
个会被执⾏,其它的规则都将不再执⾏;
9. 其它的:agenda- group 议程分组、auto-focus 焦点分组;
10. ruleflow-group 规则流,在使⽤规则流的时候要⽤到 ruleflow-group 属性,该属性的值为⼀个字符串,作⽤是⽤来将规则划分为⼀个个的组,然后在规则流当中通过使
⽤ ruleflow-group 属性的值,从⽽使⽤对应的规则,该属性会通过流程的⾛向确定要执⾏哪⼀条规则。在规则流中有具体的说明。
4. drools中相关核⼼类型说明:
1. fact:即将⼀个普通的 JavaBean 插⼊到规则的 WorkingMemory 当中后的对象(如:kieSession.inrt( javaBean对象))。规则可以对 Fact对象进⾏任意的读写操
作,当⼀个 JavaBean 插⼊到 workingMemory 当中变成 Fact 之后(返回⼀个FactHandler),Fact 对象不是原来的 JavaBean对象的副本,⽽是原来 JavaBean 对象的引⽤;
2. KieServices:就是⼀个中⼼,通过它来获取的各种对象来完成规则构建、管理和执⾏等操作;(() 获得)
3. KieContainer:是⼀个 KieBa 的容器,利⽤ KieContainer 来访问 KBa 和 KSession 等信息;(wKieContainer()获得)
4. KieBa:可以理解为是⼀个知识仓库,包含了若⼲的规则、流程、⽅法等,在 Drools 中主
要就是规则和⽅法,KieBa 本⾝并不包含运⾏时的数据之类的,如果需要执⾏规则 KieBa
中的规则的话,就需要根据 KieBa 创建 KieSession;(KieBa() 或 newKieBa()获得)
5. KieSession:就是⼀个跟 Drools 引擎打交道的会话,基于 KieBa 创建,它会包含运⾏时数据,包含“事实 Fact”,并对运⾏时数据事实进⾏规则运算;分为两类:
有状态的 KieSession(在多次与规则引擎进⾏交互中,维护会话的状态)、⽆状态的 StatelessKieSession(隔离了每次与规则引擎的交互,不会维护会话的状态);(wStatelessKieSession() 或 newKieSession()获得)
6. KieRepository:是⼀个单例对象,它是⼀个存放 KieModule 的仓库;
7. KieProject:KieContainer 通过 KieProject 来初始化、构造 KieModule,并将 KieModule 存放到 KieRepository 中,然后 KieContainer 可以通过 KieProject 来查找
KieModule 定义的信息,并根据这些信息构造 KieBa 和KieSession;
8. ClasspathKieProject:ClasspathKieProject 实现了 KieProject 接⼝,它提供了根据类路径中的 l ⽂件构造 KieModule 的能⼒,也就是我们能
够基于 Maven 构造 Drools 组件的基本保障之⼀;
3. ⼏种实现运⾏Drools规则引擎⽅法
1. 直接使⽤KieHelper动态的将规则drl字符串添加到规则引擎中并运⾏:
String drl = "package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"import java.util.List;\n" +
"rule \"test rule 1\"\n" +
"when \n" +
"$res:String() from accumulate(Message(createBy==\"zuowj\",$cont:content),init(String allContent=\"\";),action(allContent +=$cont;),result(allContent))"+
大学生个人计划"then\n" +
"System.out.println($res +\"---rule 2\");\n" +
"end";
KieBa kieBa = new KieHelper().addContent(drl, ResourceType.DRL).build();
StatelessKieSession kieSession = wStatelessKieSession();
2. 直接使⽤KieHelper动态的将drl⽂件添加到规则引擎中并运⾏:
//rule.drl⽂件(放在resources⾃定义rules⽬录中,注:路径可⾃定义)
package zuowenjun.drools.rule.demo
import del.Message;
rule "test rule2"
when
$msg:Message(createBy=="zuowj")
then
System.out.println("hello zuowj! --rule2");
$msg.tReplyBy("rule2");
end
注:如下使⽤的是wClassPathResource获取drl⽂件,其实⾥⾯封装了很多的获取资源的⽅式(如:newFileResource、newByteArrayResource、newInputStreamResource等)
//JAVA代码:
Resource resource = wClassPathResource("rules/rule.drl");
KieHelper helper = new KieHelper();
KieBa kieBa = helper.addResource(resource, ResourceType.DRL).build();
StatelessKieSession kieSession = wStatelessKieSession();
3. 直接通过drools spring配置⽂件实现规则添加及运⾏:
<!--在resources⽬录中添加drools spring的配置⽂件(如:l) -->
奶油纸杯蛋糕<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="www.springframework/schema/beans"
xmlns:xsi="www.w3/2001/XMLSchema-instance"
xmlns:kie="drools/schema/kie-spring"
xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd
drools/schema/kie-spring drools/schema/kie-spring.xsd">
<kie:kmodule id="kmodule">
<kie:kba name="kba" packages="zuowenjun.drools.rules">
</kie:kba>
</kie:kmodule>
<bean id="kiePostProcessor" class="org.kie.spring.KModuleBeanFactoryPostProcessor"/>
</beans>
JAVA代码:
//配置,此处只需通过@ImportResource导⼊配置⽂件,⾃动注册成BEAN即可,当然这⾥是⼀个单独配置⽂件,实际也可以直接放在spring boot 的applcation的启动类上即可。
@Configuration
@ImportResource("l")
public class DroolsBeansConfig {
}
//BEAN类中直接使⽤即可
@Component
public class RuleDemo {
@Autowired
private KieBa kba;//KieBa是单例
public Object checkRule(Message msg){
StatelessKieSession kieSession = wStatelessKieSession();//ssion这⾥尽可能每次都重新创建,成本也⽐较低,不要搞成单例的,这⾥是⽆状态的,⽤有状态的也⾏
return msg;
}
}
//如下是上⾯所有实例中⽤到的Message类(普通的javaBean)
public class Message {
private Long id;
private String title;
private String createBy;
private Date createDate;
private String content;
private Long enabledFlag;
private Boolean isReply;
private String replyBy;
private Date replyDate;
private String replyContent;
//省略getter、tter⽅法 ...
}
4. 还有⼀种是通过动态创建Kjar来实规则添加及运⾏,关键步骤如下:
创建 l-》创建 l-》添加规则内容-》后⾯是创建ssion-》执⾏即可;
代码就不再贴出了,可详见⽹上资源。
4. Drl规则内容⼏种写法测试代码
public Object checkRule(Object msg) {
List<String> drlContentList=new ArrayList<>();
//当⼀个Fact对象为集合对象时的判断
//这个是当把某个集合(List)当成⼀个fact传⼊⼯作内存中后,规则有效
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"import java.util.List;\n" +
"rule \"test rule 0\"\n" +
"when \n" +
"$list:List(size>0) \n" +
"$msg:Message(createBy==\"zuowj\") from $list \n " +
"then\n" +
"System.out.println(\"hello zuowj! ---rule 0\");\n" +
"$msg.tReplyBy(\"rule 0\");\n" +
"end");
//普通Pattern 模式+字段约束
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 1\"\n" +
"when \n" +
"$msg:Message(createBy==\"zuowj\")\n " +
"then\n" +
"System.out.println(\"hello zuowj! ---rule 1\");\n" +
舒的组词
"$msg.tReplyBy(\"rule 1\");\n" +
"end");
//accumulate 内联⽅式(类似for循环处理)
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 2\"\n" +
"when \n" +
"exists(Message(createBy==\"zuowj\"))\n"+
"$res:String() from accumulate(Message(createBy==\"zuowj\",$cont:content),init(String allContent=\"\";),action(allContent +=$cont;),result(allContent))"+
"then\n" +
"System.out.println($res +\"---rule 2\");\n" +
"end");
//accumulate 普通函数⽅式
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 2-2\"\n" +
"when \n" + "accumulate(Message(createBy==\"zuowj\",$id:id);$countNum:count($id);$countNum>1) \n"+
"then\n" +
"System.out.println(\"count number:\"+ $countNum +\"---rule 2-2\");\n" +
"end");
//not,不满⾜时
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 3\"\n" +
"when not Message()\n" +
"then\n" +
"System.out.println(\"no message don't say hello! ---rule 3\");\n" +
"end");
//exists,匹配执⾏⼀次
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 4\"\n" +
"when exists(Message(createBy==\"zuowj\"))\n" +
"then\n" +
"System.out.println(\"exists Message(createBy==zuowj) fact! ---rule 4\");\n" +
"end");
//forall,⼯作内存中所有fact对象必需都满⾜时才匹配规则
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 5\"\n" +
锦州笔架山
"when forall(Message(createBy==\"zuowj\"))\n" +
"then\n" +
"System.out.println(\"for all Message(createBy==zuowj) fact! ---rule 5\");\n" +
"end");
//collect,将⼯作内存中所有fact对象添加到同⼀个集合中
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import del.Message;\n" +
"rule \"test rule 6\"\n" +海尔集团是国企吗
"when Message() && $msgs:List(size>=9) from collect(Message(createBy==\"zuowj\"))\n" +
"then\n" +
"System.out.println(\"collect all Message fact(size=\" + $msgs.size() +\")! ---rule 6\");\n" +
"end");
KieHelper kieHelper=new KieHelper();
for(String drl:drlContentList){
kieHelper.addContent(drl,ResourceType.DRL);
}
KieBa kieBa = kieHelper.build();
StatelessKieSession kieSession = wStatelessKieSession();天灸
if (msg instanceof List){
} el{
}
return msg;
}
//单元测试
/**
* @author zuowenjun
*/
@RunWith(SpringRunner.class)
@SpringBootTest(class = {DroolsDemoApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class RuleTests {
@Autowired
private RuleDemo ruleDemo;
@Test
public void testRule() {
List<Message> msgList=new ArrayList<>();
for(int i=1;i<=10;i++) {
int n=i;
Message msg = new Message() {
{
tCreateBy("zuowj");
tContent("hello drools" + String.valueOf(n));
}
};
if (n==1){
msg.tCreateBy("zuowenjun");
}
msgList.add(msg);
}
Object obj = ruleDemo.checkRule(msgList);
System.out.println(JsonUtils.derializer(obj));
}
}
5. 规则引擎引发的举⼀反三,⾃⼰实现⼀个规则引擎
思路:1.定义规则内容(即:规则执⾏单元),2.定义贯穿整个规则执⾏链条的上下⽂,内部就放fact、global等,具体实现参照如下⽰例代码【注意:如果仅是⽰例测试代码,并不规范,仅为演⽰提供思路】,整个规则执⾏采取:责任链的设计模式,即:每个规则只负责满⾜⾃⼰条件的执⾏逻辑,
最后更新上下⽂中相关的内容。
//规则链上下⽂,⾥⾯就包含fact集合,全局对象及执⾏过的rule
public class RuleChainContext {
public List<Object> factList;
public static Map<String, Object> global;
public RuleUnit execedRule;
}
//规则执⾏单元抽象类(这⾥⽤抽象类⽽没有⽤接⼝,是因为我要限定组织逻辑,可以理解为模板⽤法)
public abstract class RuleUnit {
public RuleUnit nextExecedRule;
protected String name;
public abstract String getName();
public abstract boolean matchWhen(RuleChainContext context);
public abstract void doThen(RuleChainContext context);
public final void execute(RuleChainContext context) {
if (matchWhen(context)) {
doThen(context);
}
if (dRule == null) {
}
}
}
通过单元测试模拟调⽤:
@Test
public void testDefRules() {
List<RuleUnit> ruleUnitList = new ArrayList<>();
ruleUnitList.add(new RuleUnit() {
@Override
public String getName() {
name= "rule-1";
return name;
}
@Override
public boolean matchWhen(RuleChainContext context) {
return context.factList.stream().anyMatch(f->f instanceof Integer && 1==(Integer)f);
}
@Override
public void doThen(RuleChainContext context) {
System.out.println("rule[include 1] do");
//TODO:context
}
});
ruleUnitList.add(new RuleUnit() {
@Override
public String getName() {
name= "rule-2";
return name;
}
@Override
public boolean matchWhen(RuleChainContext context) {
return context.factList.stream().anyMatch(f->f instanceof Integer && 2==(Integer)f);
}
@Override
public void doThen(RuleChainContext context) {
System.out.println("rule[include 2] do");
//TODO:context
}
});
RuleChainContext context=new RuleChainContext();
context.factList=new ArrayList<>();
context.factList.add(1);//加⼊1则触发规则1
context.factList.add(2);//加⼊2则触发规则2,若减少规则相应减少
for(RuleUnit ruleUnit:ruleUnitList){
}
System.out.println("result context:\n" + JsonUtils.derializer(context));
}
最终结果:
rule[include 1] do
rule[include 2] do
result context:
{"factList":[1,2],"execedRule":{"nextExecedRule":{"nextExecedRule":null,"name":"rule-2"},"name":"rule-1"}}
从输出的结果可以看出,还是可以达到规则引擎的简单效果的,当然如果想在⽣产环境实际应⽤⾃⼰实现的“类规则引擎”代码,实现规则与执⾏分开,也可将规则执⾏单元(RuleUnit)实现类单独放到⼀个JAR包,然后再借助于URLClassLoader实现动态加载并添加⾃定义的实现规则执⾏单元(RuleUnit)的类,最后执⾏即可。【⽅⾯的同学实现亦同理】
注:⽂中相关名词解释来源于⽹上,并⾮原创,我这⾥仅为知识点总结!
可参考相关drools系列⽂章: