Springboot集成规则引擎Drools方式

 更新时间:2023年11月27日 16:51:26   作者:Leopard锋  
这篇文章主要介绍了Springboot集成规则引擎Drools方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。

Drools 允许使用声明方式表达业务逻辑。

可以使用非 XML 的本地语言编写规则,从而便于学习和理解。

并且,还可以将 Java 代码直接嵌入到规则文件中。

详细可见开源业务规则引擎:Drools中文网

一、项目目录结构

二、集成drools

1、引入依赖

 
        <!--drools规则引擎-->
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-core</artifactId>
            <version>7.59.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>7.59.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-templates</artifactId>
            <version>7.59.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-api</artifactId>
            <version>7.59.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-spring</artifactId>
            <version>7.59.0.Final</version>
        </dependency>

2、新建规则文件

rule1.drl

package com.leopard.drools
import com.leopard.drools.pojo.QueryParam
import com.leopard.drools.service.RuleEngineService
 
dialect  "java"
 
rule "boy"
    when queryParam : QueryParam(paramId != null && paramSign.equals("+"))
    then
        RuleEngineService ruleEngineService = new RuleEngineService();
        ruleEngineService.executeAddRule(queryParam);
        System.out.println("参数:getParamId="+queryParam.getParamId()+";getParamSign="+queryParam.getParamSign());
end

rule2.drl

package com.leopard.drools
import com.leopard.drools.pojo.QueryParam
 
dialect  "java"
rule "girl"
    when queryParam : QueryParam(paramId != null && paramSign.equals("-"))
    then
        System.out.println(queryParam.getParamId() +  "是女孩");
end

3、创建KieUtils(因要做热加载,需要重载规则文件,规则引擎容器要支持变动)

public class KieUtils {
 
    private static KieContainer kieContainer;
 
    private static KieSession kieSession;
 
    private static KModuleBeanFactoryPostProcessor kModuleBeanFactoryPostProcessor;
 
    public static KieContainer getKieContainer() {
        return kieContainer;
    }
 
    public static void setKieContainer(KieContainer kieContainer) {
        KieUtils.kieContainer = kieContainer;
        kieSession = kieContainer.newKieSession();
    }
 
    public static KieSession getKieSession() {
        return kieSession;
    }
 
    public static void setKieSession(KieSession kieSession) {
        KieUtils.kieSession = kieSession;
    }
 
    public static KieServices getKieServices() {
        return KieServices.Factory.get();
    }
 
    public static KModuleBeanFactoryPostProcessor getkModuleBeanFactoryPostProcessor() {
        return kModuleBeanFactoryPostProcessor;
    }
 
    public static void setkModuleBeanFactoryPostProcessor(KModuleBeanFactoryPostProcessor kModuleBeanFactoryPostProcessor) {
        KieUtils.kModuleBeanFactoryPostProcessor = kModuleBeanFactoryPostProcessor;
    }
}

4、添加初始化配置

 
@Slf4j
@Configuration
public class RuleEngineConfig {
 
    public static final String RULES_PATH = "droolsRule/";
    public static final String BASE_RULES_PATH = "classpath*:";
 
    private final KieServices kieServices = KieServices.Factory.get();
 
    /**
     * @return
     * @throws IOException
     * @ConditionalOnMissingBean,它是修饰bean的一个注解, 主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,
     * 它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常
     */
    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        //获取初始化规则文件所在路径
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] files = resourcePatternResolver.getResources(BASE_RULES_PATH + RULES_PATH + "*.*");
        String path = null;
        for (Resource file : files) {
            path = RULES_PATH + file.getFilename();
            log.info("path=" + path);
            //将规则文件写规则引擎系统内
            kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
        }
        return kieFileSystem;
    }
 
    /**
     * 创建KIE内部容器
     *
     * @return
     * @throws IOException
     */
    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
 
        final KieRepository kieRepository = kieServices.getRepository();
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        KieContainer kieContainer = kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
        KieUtils.setKieContainer(kieContainer);
        return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
    }
 
    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }
 
    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException {
        KieSession kieSession = kieContainer().newKieSession();
        KieUtils.setKieSession(kieSession);
        return kieSession;
    }
 
    @Bean
    @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kModuleBeanFactoryPostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

5、重载实现,可通过链接数据库刷新规则

@Slf4j
@Service
public class ReloadDroolsRules {
 
    @Autowired
    private KieSession kieSession;
 
    @Autowired
    private KieContainer kieContainer;
 
    /**
     * 重新加载规则
     * @param drlName   规则名称
     * @throws Exception
     */
    public void reload(String drlName) throws Exception {
 
        KieFileSystem kfs = KieUtils.getKieServices().newKieFileSystem();
//        loadDBRules(drlName, kfs);
        loadFileRules(drlName, kfs);
        KieBuilder kieBuilder = KieUtils.getKieServices().newKieBuilder(kfs).buildAll();
        Results results = kieBuilder.getResults();
        if (results.hasMessages(Message.Level.ERROR)) {
            System.out.println(results.getMessages());
            throw new IllegalStateException("### errors ###");
        }
        KieContainer kieContainer = KieUtils.getKieServices().newKieContainer(KieUtils.getKieServices().getRepository().getDefaultReleaseId());
        KieUtils.setKieContainer(kieContainer);
        System.out.println("新规则重载成功");
    }
 
    /**
     * 重新读取数据库配置内容
     * @param drlName
     * @param kfs
     * @throws IOException
     */
    private void loadDBRules(String drlName, KieFileSystem kfs) throws IOException {
        //        String path = "src/main/resources/rules/address.drl";
        String path = "src/main/resources/" + RuleEngineConfig.RULES_PATH + "/" + drlName + ".drl";
        // 从数据库加载的规则
        kfs.write(path, "package plausibcheck.adress\n\n import com.leopard.drools.pojo.QueryParam;\n\n rule \"Postcode 6 numbers\"\n\n    when\n  then\n        System.out.println(\"打印日志:更新rules成功!\");\n end");
    }
 
    /**
     * 重新配置文件
     * @param drlName
     * @param kfs
     * @throws IOException
     */
    private void loadFileRules(String drlName, KieFileSystem kfs) throws IOException {
        // 从classess/rules加载的规则
        //获取初始化规则文件所在路径
        String path = null;
        for (Resource file : getRuleFiles(drlName)) {
            path = RuleEngineConfig.RULES_PATH + file.getFilename();
            log.info("path=" + path);
            //将规则文件写规则引擎系统内
            kfs.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
        }
    }
 
    private Resource[] getRuleFiles(String drlName) throws IOException {
        if (StringUtils.isEmpty(drlName)) {
            ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            return resourcePatternResolver.getResources(RuleEngineConfig.BASE_RULES_PATH + RuleEngineConfig.RULES_PATH + "**/*.*");
        }
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources(RuleEngineConfig.BASE_RULES_PATH + RuleEngineConfig.RULES_PATH + "**/" + drlName + ".*");
    }
}

三、验证测试

1、创建测试实体类

@Data
public class QueryParam {
 
    private String paramId;
 
    private String paramSign;
}

2、添加规则实现

@Slf4j
@Service
public class RuleEngineService {
 
    /**
     * 插入规则
     *
     * @param param
     */
    public void executeAddRule(QueryParam param) {
        log.info("参数数据:" + param.getParamId() + ";" + param.getParamSign());
        log.info("插入规则");
    }
 
    /**
     * 移除规则
     *
     * @param param
     */
    public void executeRemoveRule(QueryParam param) {
        log.info("参数数据:" + param.getParamId() + ";" + param.getParamSign());
        log.info("移除规则");
    }
}

3、API调用实现

@RestController
@RequestMapping(value = "test")
public class TestController {
 
    @Autowired
    private RuleEngineService ruleEngineService;
 
    @Autowired
    private ReloadDroolsRules reloadDroolsRules;
 
    @RequestMapping("/param")
    public void param (){
        QueryParam queryParam1 = new QueryParam() ;
        queryParam1.setParamId("1");
        queryParam1.setParamSign("+");
        QueryParam queryParam2 = new QueryParam() ;
        queryParam2.setParamId("2");
        queryParam2.setParamSign("-");
        QueryParam queryParam3 = new QueryParam() ;
        queryParam3.setParamId("3");
        queryParam3.setParamSign("-");
        // 入参
        KieUtils.getKieSession().insert(queryParam2) ;
        KieUtils.getKieSession().insert(queryParam3) ;
        KieUtils.getKieSession().insert(queryParam1) ;
        KieUtils.getKieSession().insert(this.ruleEngineService) ;
        // 返参
        KieUtils.getKieSession().fireAllRules() ;
    }
 
    @RequestMapping("/reload")
    public String reload (String ruleName) throws Exception {
        // 返参
        reloadDroolsRules.reload(ruleName);
        return "新规则重载成功";
    }
 
}

运行服务,查看结果

调用 localhost:9666/api/v1/test/param

修改规则文件,调用重新加载配置 localhost:9666/api/v1/test/reload,不指定配置规则文件名,默认重新加载全部规则,然后重新调用请求

需要注意点:

修改规则配置,是修改加载后的文件,也就是 运行项目时,生成的 target目录下加载的规则文件,而不是项目本身resources下的。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 解决Maven build打包,类找不到,找不到引用的那个项目里面的类问题

    解决Maven build打包,类找不到,找不到引用的那个项目里面的类问题

    文章讨论了在使用Maven构建项目时遇到类找不到的问题,并给出了解决方法,主要原因是公共项目被打成了可执行的jar,导致其他项目无法引用其中的类,解决方法包括在公共项目的pom中将打包类型改为普通jar,以及在主项目中正确引用公共项目
    2026-02-02
  • FuncGPT慧函数保护数据安全提高代码质量减少软件故障(java示例)

    FuncGPT慧函数保护数据安全提高代码质量减少软件故障(java示例)

    这篇文章主要为大家介绍了FuncGPT慧函数保护数据安全提高代码质量减少软件故障(java示例),有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Spring MVC Controller返回值及异常的统一处理方法

    Spring MVC Controller返回值及异常的统一处理方法

    这篇文章主要给大家介绍了关于Spring MVC Controller返回值及异常的统一处理方法,文中通过示例代码介绍的非常详细,对大家的学习或者使用Spring MVC具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-11-11
  • try-with-resource优雅关闭io流的方法

    try-with-resource优雅关闭io流的方法

    这篇文章主要给大家介绍了关于try-with-resource优雅关闭io流的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • SpringBoot开发中使用DTO层的方法示例

    SpringBoot开发中使用DTO层的方法示例

    DTO层是在应用程序的业务逻辑层和数据访问层之间引入的一个中间层,用于在不同层之间传输数据,本文主要介绍了SpringBoot开发中使用DTO层,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • Java for循环和foreach循环的性能对比分析

    Java for循环和foreach循环的性能对比分析

    这篇文章主要介绍了Java for循环和foreach循环的性能对比分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java编程实现帕斯卡三角形代码示例

    Java编程实现帕斯卡三角形代码示例

    这篇文章主要介绍了Java编程实现帕斯卡三角形代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 关于Java的对象序列化流和反序列化流详细解读

    关于Java的对象序列化流和反序列化流详细解读

    这篇文章主要介绍了关于Java的对象序列化流和反序列化流,对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象,反之,自己序列还可以从文件中读取回来,重构对象,对它进行反序列化,需要的朋友可以参考下
    2023-05-05
  • 解决springcloud启动时报错Connection refused:connect问题

    解决springcloud启动时报错Connection refused:connect问题

    这篇文章主要介绍了解决springcloud启动时报错Connection refused:connect问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Java中MyBatis Plus知识点总结

    Java中MyBatis Plus知识点总结

    在本篇文章里小编给大家整理一篇关于Java中MyBatis Plus知识点总结,需要的朋友们参考下。
    2019-10-10

最新评论