对于Drools后期新增规则动态加载的方法

场景描述:

需要判断的规则有上千上万个或者更多。

举个例子:法律案件或者交通法规,像这类场景,每一个法条都相当于是一个条件。(如:是什么原因导致违章的?是闯了红灯啊,还是超速了呀还是超载了呀等等等)各种if-else或者switch-case判断。

这些是已有的规则判断,好说,弄个Excel表格或者插入到库中,一次性读出来,用kie提供的方式直接用就行了。但是如果后期有新增怎么办?

本文只分享新增的场景解决的方案。更新和删除部分目前还没找解决方法。



pom文件

        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    4.0.0

   

        org.springframework.boot

        spring-boot-starter-parent

        2.2.5.RELEASE

       

   

    com.***.drools

    rule-engine

    1.0.0

    rule-engine

    Demo project for Spring Boot

   

        1.8

        7.33.0.Final

   

   

       

            javax.inject

            javax.inject

            1

       

       

            org.springframework.boot

            spring-boot-starter-web

       

       

            org.mybatis.spring.boot

            mybatis-spring-boot-starter

            2.1.2

       

       

            mysql

            mysql-connector-java

            8.0.19

       

       

            org.projectlombok

            lombok

            true

       

       

            org.springframework.boot

            spring-boot-starter-test

            test

           

               

                    org.junit.vintage

                    junit-vintage-engine

               

           

       

       

       

            org.kie

            kie-api

            ${drools.version}

       

       

            org.drools

            drools-core

            ${drools.version}

       

       

            org.drools

            drools-compiler

            ${drools.version}

       

       

            org.drools

            drools-decisiontables

            ${drools.version}

       

       

            org.drools

            drools-templates

            ${drools.version}

       

       

            org.kie

            kie-internal

            ${drools.version}

       

       

            org.mvel

            mvel2

            2.4.4.Final

       

       

            com.thoughtworks.xstream

            xstream

            1.4.11.1

       

       

       

            org.slf4j

            slf4j-api

       

   

   

       

           

                org.springframework.boot

                spring-boot-maven-plugin

           

       

   


实现思路:

1. 使用KieHelper来build()。

故此KieHelper要注册成一个Bean。能保持单利,保证后期动态编译的时候不会丢失已编译过的规则。 

@Bean

public KieHelper kieHelper() {

    return new KieHelper();

}

DataObject-实体封装类

@Data

public class DataObject {

    private String ly;

}

RuleTemplate.java--规则封装类

public class RuleTemplate {

    private String template;

    public RuleTemplate(String template) {

        this.template = template;

    }

    public String getTemplate() {

        return template;

    }

    public void setTemplate(String template) {

        this.template = template;

    }

}

RuleTemplate的构造器里的参数其实就是Drl文件的字符串形式。根据自己的业务去拼。我这块是直接去读的库。

@Bean

public RuleTemplate ruleTemplate() {

    RuleTemplate ruleTemplate = new RuleTemplate(CreateRule.getInstance().ruleTemplate(IRulesService.queryAll()));

    return ruleTemplate;

}

2. 容器初始化的时候去读取已有的规则数据(数据库或者Excel都行),我是直接读的库。

    2.1 查完拼接成规则字符串(拼完后可以弄到drl文件里试试规则生成的是否有问题)。

    2.2 使用kieHelper来添加已经拼好的rule字符串。然后在build。

@Bean

@PostConstruct

public KieBase kieBase() {

    //动态加载的核心实现方式

    kieHelper.addContent(ruleTemplate.getTemplate(), ResourceType.DRL);//添加规则

    KieBase kieBase = kieHelper.build();//手动编译规则

    return kieBase;

}

到这块是项目启动阶段完成的,预加载已知的规则。


以下部分是项目运行阶段来动态的去添加规则后编译并查找。

1. 新规则字符串。

String test = "package com.pkulaw.drools.rules;\n" +

        "\n" +

        "import com.pkulaw.drools.entity.DataObject\n" +

        "\n" +

        "global java.util.List lyList;\n" +

        "\n" +

        "rule \"ly-rule-0000\"\n" +

        "agenda-group \"ly-group\"\n" +

        "dialect \"java\"\n" +

        "when\n" +

        "    d : DataObject(ly == \"测试\")\n" +

        "then\n" +

        "    lyList.add(\"这是测试\");    \n" +

        "end";

2. KieHelper来添加和编译。

kieHelper.addContent(ruleContent, ResourceType.DRL);

//重编译

KieBase kieBase1 = kieHelper.build();

//重新注册单利

beanRegister(kieBase1);

重新注册单利的原因是每次kieHelper去build的时候会重新生成一个KieBase,而KieBase是去获取session进行fact匹配的关键。故此,要去替换Spring容器中的bean的实例。

3.1.1 替换单利bean(Deprecated)-针对Kiebase

//这个方式不够优雅,但也不是不可行,故此留着这段。

public synchronized void beanRegister(KieBase newKBase) {

    DefaultListableBeanFactory factory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();

    factory.removeBeanDefinition("kieBase");

    BeanDefinitionBuilder beanDefinitionBuilder =

            BeanDefinitionBuilder.genericBeanDefinition(newKBase.getClass().getName());

    // get the BeanDefinition

    BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

    factory.registerBeanDefinition("kieBase", beanDefinition);

    factory.registerSingleton("kieBase", newKBase);

}

3.1.2 替换KieBase

上面对直接Spring容器的bean替换不够好,死脑筋没转过来。群友一提醒立马醒悟。

public class KieBaseTemplate {

    private KieBase kieBase;

    public KieBaseTemplate(KieBase kieBase) {

        this.kieBase = kieBase;

    }

    public KieBase getKieBase() {

        return kieBase;

    }

    public void update(KieBase kieBase) {

        this.kieBase = kieBase;

    }

}

原来是Kiebase注册成bean现在改成把KieBaseTemplate 注册成bean。

@Bean

@PostConstruct

public KieBaseTemplate kieBaseTemplate() {

    kieHelper.addContent(ruleTemplate.getTemplate(), ResourceType.DRL);

    KieBase kieBase = kieHelper.build();

    return new KieBaseTemplate(kieBase);

}

每次新增规则后build的时候把生成的kieBase通过update的方式重新替换掉就好了。

查找的时候通过kieBaseTemplate的getKieBase方法获取KieBase。

(有时候换个思路去思考能写出更优雅的代码来,这一段就比直接操作Bean优雅多了)

以下是查找测试代码

KieSession kSession = kieBaseTemplate.getKieBase().newKieSession();

DataObject obj1 = new DataObject();

obj1.setLy("测试");

List lyList = new ArrayList();

kSession.setGlobal("lyList", lyList);

kSession.insert(obj1);

kSession.fireAllRules();

System.out.println(lyList);

总结

 这段时间由于公司需求去研究一下这个工具,基础知识阅读官方文档即可,实现方式有多重,官网介绍有InternalKnowledgeBase来实现的,还有一种是KieBuilder来实现的。KieHelper是我在GitHub(他的地址)上借鉴某位大佬的实现方式所得到的启发。

欢迎评论和批评,菜鸟一枚,有错误及时修正,以免误导他人。

你可能感兴趣的:(对于Drools后期新增规则动态加载的方法)