目录
Drools规则引擎,网上大把相关的文章介绍,但我感觉不够直白,理解有些困难,且知识点没有集中比较分散、有些还是引版本的内容,对与新手来说上手可能比较慢,而且比较容易走弯路,故我在深入研究并实践于项目中后,在空闲时间花费精力整理了这篇文章,分享出来,便大家快速上手。
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.55.0.Final</version>
</dependency>
DRL文件基本格式:
package rules.testwrod //包名,必需,这是逻辑上,与物理路径无关
import xxxxx; //可选,导入要使用的类名(还支持直接导入静态方法)
global java.util.List myGlobalList;//可选,定义全局变量(该变量由外部setGlobal传入)
function getResult(...){ //可选,自定义函数
}
query "query_gt_0"(...) //可选,自定义查询(仅只有LHS内容)
$result:规则Pattern
end
rule “test001” //规则名称,必需,且需唯一
when //规则开始关键字,必需
//这里如果为空 则表示 eval(true); LHS内容(即:规则条件)
then //规则条件结束关键字,必需,后面部份则是RHS内容(即:规则触发的逻辑)
System.out.println(“hello drools!”);
end //规则结束关键字
涉及的名词解释:
Drools的属性说明(一般在在rule 名称 与when之前设置属性):
drools中相关核心类型说明:
直接使用KieHelper动态的将规则drl字符串添加到规则引擎中并运行:
String drl = "package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.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"; KieBase kieBase = new KieHelper().addContent(drl, ResourceType.DRL).build();
StatelessKieSession kieSession = kieBase.newStatelessKieSession();
kieSession.execute(list);</code></pre></li>
直接使用KieHelper动态的将drl文件添加到规则引擎中并运行:
//rule.drl文件(放在resources自定义rules目录中,注:路径可自定义)
package zuowenjun.drools.rule.demo
import cn.zuowenjun.model.Message;
rule "test rule2"
when
$msg:Message(createBy=="zuowj")
then
System.out.println("hello zuowj! --rule2");
$msg.setReplyBy("rule2");
end
注:如下使用的是ResourceFactory.newClassPathResource获取drl文件,其实里面封装了很多的获取资源的方式(如:newFileResource、newByteArrayResource、newInputStreamResource等)
//JAVA代码:
Resource resource = ResourceFactory.newClassPathResource("rules/rule.drl");
KieHelper helper = new KieHelper();
KieBase kieBase = helper.addResource(resource, ResourceType.DRL).build();
StatelessKieSession kieSession = kieBase.newStatelessKieSession();
kieSession.execute(msg);
直接通过drools spring配置文件实现规则添加及运行:
<!--在resources目录中添加drools spring的配置文件(如:spring-drools.xml) -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:kie="http://drools.org/schema/kie-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://drools.org/schema/kie-spring http://drools.org/schema/kie-spring.xsd">
<kie:kmodule id="kmodule">
<kie:kbase name="kbase" packages="zuowenjun.drools.rules">
</kie:kbase>
</kie:kmodule>
<bean id="kiePostProcessor" class="org.kie.spring.KModuleBeanFactoryPostProcessor"/>
</beans>
JAVA代码:
//配置,此处只需通过@ImportResource导入配置文件,自动注册成BEAN即可,当然这里是一个单独配置文件,实际也可以直接放在spring boot 的applcation的启动类上即可。
@Configuration
@ImportResource("classpath:spring-drools.xml")
public class DroolsBeansConfig {
}
//BEAN类中直接使用即可
@Component
public class RuleDemo {
@Autowired
private KieBase kbase;//KieBase是单例 public Object checkRule(Message msg){
StatelessKieSession kieSession = kbase.newStatelessKieSession();//session这里尽可能每次都重新创建,成本也比较低,不要搞成单例的,这里是无状态的,用有状态的也行
kieSession.execute(msg);
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、setter方法 ...
}
还有一种是通过动态创建Kjar来实规则添加及运行,关键步骤如下:
创建 pom.xml-》创建 kmodule.xml-》添加规则内容-》后面是创建session-》执行即可;
代码就不再贴出了,可详见网上资源。
public Object checkRule(Object msg) {
List<String> drlContentList=new ArrayList<>();
//当一个Fact对象为集合对象时的判断
//这个是当把某个集合(List)当成一个fact传入工作内存中后,规则有效
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.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.setReplyBy(\"rule 0\");\n" +
"end");
//普通Pattern 模式+字段约束
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.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.setReplyBy(\"rule 1\");\n" +
"end");
//accumulate 内联方式(类似for循环处理)
drlContentList.add("package zuowenjun.drools.rule.demo\n" +
"import cn.zuowenjun.model.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 cn.zuowenjun.model.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 cn.zuowenjun.model.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 cn.zuowenjun.model.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 cn.zuowenjun.model.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 cn.zuowenjun.model.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);
}
KieBase kieBase = kieHelper.build();
StatelessKieSession kieSession = kieBase.newStatelessKieSession();
if (msg instanceof List){
kieSession.execute((List<?>)msg);
} else{
kieSession.execute(msg);
}
return msg;
}
//单元测试
/**
* @author zuowenjun
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {FmsHelperApplication.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() {
{
setCreateBy("zuowj");
setContent("hello drools" + String.valueOf(n));
}
};
if (n==1){
msg.setCreateBy("zuowenjun.cn");
}
msgList.add(msg);
}
Object obj = ruleDemo.checkRule(msgList);
System.out.println(JsonUtils.deserializer(obj));
}
}
思路: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 (context.execedRule == null) {
context.execedRule = this;
}
context.execedRule.nextExecedRule = this;
}
}
通过单元测试模拟调用:
@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[exclude 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){
ruleUnit.execute(context);
}
System.out.println("result context:\n" + JsonUtils.deserializer(context));
}
最终结果:
rule[include 1] do
rule[exclude 2] do
result context:
{"factList":[1,2],"execedRule":{"nextExecedRule":{"nextExecedRule":null,"name":"rule-2"},"name":"rule-1"}}
从输出的结果可以看出,还是可以达到规则引擎的简单效果的,当然如果想在生产环境实际应用自己实现的“类规则引擎”代码,实现规则与执行分开,也可将规则执行单元(RuleUnit)实现类单独放到一个JAR包,然后再借助于URLClassLoader实现动态加载并添加自定义的实现规则执行单元(RuleUnit)的类,最后执行即可。【.NET方面的同学实现亦同理】
注:文中相关名词解释来源于网上,并非原创,我这里仅为知识点总结!
可参考相关drools系列文章:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章