规则引擎Drools学习与实例

一、概述

drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中)使得业务规则的变更不需要修改项目代码、不用重启服务器就可以在线上环境立即生效。

规则引擎的诞生

针对复杂的业务规则代码处理,往往存在一下问题:
1、硬编码实现业务规则难以维护;
2、硬编码实现业务规则难以应对变化;
3、业务规则发生变化需要修改代码,重启服务后才能生效;
于是规则引擎Drools便诞生了

规则引擎的优势

1、业务规则与系统代码分离,实现业务规则的集中管理
2、在不重启服务的情况下可随时对业务规则进行扩展和维护
3、可以动态修改业务规则,从而快速响应需求变更
4、规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则
5、减少了硬编码业务规则的成本和风险
6、使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单

drools组成结构

  • Working Memory:工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,所以我们开发的应用程序只需要将我们的数据插入到Working Memory中即可;
  • Rule Base:规则库,我们在规则文件中定义的规则都会被加载到规则库中
  • Inference Engine:推理引擎

二、基础语法

关键字含义
package包路径。建议该路径写我们的逻辑代码路径
dialect语言,指定目前系统语言
import导入规则文件需要使用到的外部变量,和Java的引入一样
rule规则体,以rule开头,以end结尾。后面一般会跟一只字符串的值,建议字符串的值写规则名称,或者描述什么规则
when条件,这里如果为空,则表示eval(true)
then符合条件之后执行的逻辑
变量名以$符号开始后面跟我们Java中的对象

注意:

  • 注释的使用,单行// 多行/**/
  • 单个drl文件可以书写多个规则
  • 条件允许为空

三、实例

规则引擎的使用分为两种:

  • 第一种:直接加载classPath下的规则(即drl文件)
  • 第二种:编程方式动态加载规则

resources下增加文件/META-INF/kmodule.xml(这里空,后面newKieSession()不给参数就默认就行了)

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
</kmodule>

实体

@Data
public class Person {
    private String name;
    private int age;
}

第一种:直接加载classPath下的规则(即drl文件)

  • 测试代码
@Test
    void contextLoads() {
        KieServices kieServices = KieServices.Factory.get();
        // 加载classPath下的规则
        container = kieServices.getKieClasspathContainer();
        // 编译规则,若通过则生成会话
        statefulKieSession = container.newKieSession();
        Person person = new Person();
        person.setAge(12);
        person.setName("Test");
        statefulKieSession.insert(person);
        // 执行规则引擎
        statefulKieSession.fireAllRules();
        statefulKieSession.dispose();
    }
  • 规则文件
    在这里插入图片描述
  • 规则文件test.drl
import com.lw.pojo.Person

dialect  "mvel"

rule "age"
    when
        $person : Person(age<16 || age>50)
    then
        System.out.println("这个人的年龄不符合要求!(基于规则文件)");
end

第二种:编程方式动态加载规则

@Test
    public void ruleStringTest() throws Exception {
    // 基于此可以通过ftl,动态生成,然后保存到数据库,初始化Spring容器时加载就可以了
        String myRule = "import com.lw.pojo.Person\n" +
                "\n" +
                "dialect  \"mvel\"\n" +
                "\n" +
                "rule \"age\"\n" +
                "    when\n" +
                "        $person : Person(age<16 || age>50)\n" +
                "    then\n" +
                "        System.out.println(\"这个人的年龄不符合要求!(基于动态加载)\");\n" +
                "end\n";
        KieHelper helper = new KieHelper();
        helper.addContent(myRule, ResourceType.DRL);
        KieSession ksession = helper.build().newKieSession();
        Person person = new Person();
        person.setAge(12);
        person.setName("Test");
        ksession.insert(person);
        ksession.fireAllRules();
        ksession.dispose();
    }

第二种(扩展)

  • 初始化Spring容器时加载规则drl到容器中
/**
 * 初始化Spring容器时加载规则drl到容器中
 */
@Configuration
public class KiaSessionConfig {

    private static final String RULES_PATH = "rules/";

    @Bean
    public KieFileSystem kieFileSystem() throws IOException {
        // KieFileSystem 是一个内存文件系统,用于以编程方式定义, 组成 KieModule 的资源
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        final Resource[] resources = resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
        return resources;
    }

    @Bean
    public KieContainer kieContainer() throws IOException {

        final KieRepository kieRepository = getKieServices().getRepository();
        kieRepository.addKieModule(new KieModule() {
            public ReleaseId getReleaseId() {
                return kieRepository.getDefaultReleaseId();
            }
        });
        // 解析drl文件内容并构建规则
        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());

    }

    private KieServices getKieServices() {
        return KieServices.Factory.get();
    }
    // 将ftl动态生成的规则存储到数据库时,用于初始化加载全部规则
    @Bean
    public KieBase kieBase() {
        /**
         * KieBase 是所有应用程序知识定义的存储库。
         * 它将包含规则、流程、功能、类型模型。
         * KieBase 本身不包含运行时数据,而是从 KieBase 创建会话可以在其中插入数据并启动流程实例。
         */
        KieBase kieBase = getKieServices().getKieClasspathContainer().getKieBase();

        // 查询出所有规则
//        List<Rule> rules = ruleMapper.selectAll();
//        // 分别编译(分别处理编译错误)
//        rules.forEach(item ->
// 		 构建通过编程方式kieHelper.addContent(definition, ResourceType.DRL);
//                DroolsUtil.buildRule(kieBase, item.getName(), item.getDefinition())
//        );`在这里插入代码片`

        return kieBase;
    }
    // 注册到bean中,方便自动装配直接使用
    @Bean
    public KieSession kieSession() throws IOException {
    	// 为这个 KieContainer 创建默认的 KieSession
        return kieContainer().newKieSession();
    }
}
  • 实体
@Data
public class User {
    private String name;
    private String address;
    private String sex;
}
  • 规则rule.drl
package com.lw.rules

import com.lw.pojo.User
dialect "java"

rule "address eq bsg"
    when
        $user: User(address == "北京" || address == "上海" || address == "广东")
    then
         System.out.println($user.getName() + "在北上广相信眼泪");
    end

rule "address eq man"
    when
        $user: User(sex == "1")
    then
         System.out.println($user.getName() + "是男性");
    end

rule "address eq woman"
    when
        $user: User(sex == "2")
    then
         System.out.println($user.getName() + "是女性");
    end
  • 执行规则
@Test
    void test1() {
        User user = new User();
        user.setAddress("广东");
        user.setName("wh");
        user.setSex("1");
        // 插入用户
        session.insert(user);
        // 执行规则
        session.fireAllRules();
    }

在这里插入图片描述

代码放置在gitee,仓库地址:https://gitee.com/lwstudy/study

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值