1. 概述

Drools 是一个业务规则管理系统(BRMS)解决方案。它提供了一个规则引擎,用于处理事实(Facts)并根据规则与事实的处理结果生成输出。通过集中化业务逻辑,可以快速、低成本地引入变更。

它还通过提供易于理解的规则编写格式,弥合了业务团队与技术团队之间的鸿沟。

2. Maven 依赖

要开始使用 Drools,首先需要在 pom.xml 中添加几个依赖:

<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-ci</artifactId>
    <version>9.44.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-decisiontables</artifactId>
    <version>9.44.0.Final</version>
</dependency>

两个依赖的最新版本可在 Maven 中央仓库获取:kie-cidrools-decisiontables

3. Drools 基础概念

以下是 Drools 的核心概念:

  • 事实(Facts):表示作为规则输入的数据
  • 工作内存(Working Memory):存储事实的容器,用于模式匹配,支持修改、插入和删除操作
  • 规则(Rule):关联事实与匹配操作的单个规则,可通过 .drl 文件(Drools 规则语言)或 Excel 电子表格中的决策表(Decision Table)定义
  • 知识会话(Knowledge Session):持有触发规则所需的所有资源;所有事实插入会话后,匹配的规则被触发
  • 知识库(Knowledge Base):表示 Drools 生态系统中的知识,包含规则资源的定位信息,并创建知识会话
  • 模块(Module):包含多个知识库,每个知识库可持有不同会话

4. Java 配置

要在给定数据上触发规则,需要实例化框架提供的类,并传入规则文件位置和事实信息:

4.1 KieServices

KieServices 类提供对所有 Kie 构建和运行时设施的访问。它提供多个工厂、服务和实用方法。首先获取 KieServices 实例:

KieServices kieServices = KieServices.Factory.get();

通过 KieServices 创建 KieFileSystemKieBuilderKieContainer 的新实例。

4.2 KieFileSystem

设置 KieFileSystem:这是框架提供的内存文件系统。以下代码以编程方式定义 Drools 资源(如规则文件和决策表)的容器:

private KieFileSystem getKieFileSystem() {
    KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
    List<String> rules = Arrays.asList("com/baeldung/drools/rules/BackwardChaining.drl", "com/baeldung/drools/rules/SuggestApplicant.drl", "com/baeldung/drools/rules/Product_rules.drl.xls");
    for (String rule : rules) {
        kieFileSystem.write(ResourceFactory.newClassPathResource(rule));
    }
    return kieFileSystem;
}

这里从 classpath 读取文件(Maven 项目中通常是 /src/main/resources)。

4.3 KieBuilder

通过 KieBuilder 构建 KieFileSystem 的内容,使用 kb.buildAll() 编译所有规则并检查语法错误:

KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
kieBuilder.buildAll();

4.4 KieRepository

框架自动将构建生成的 KieModule 添加到 KieRepository

KieRepository kieRepository = kieServices.getRepository();

4.5 KieContainer

使用 KieModuleReleaseId 创建新的 KieContainer。此处 Kie 分配默认 ReleaseId

ReleaseId krDefaultReleaseId = kieRepository.getDefaultReleaseId();
KieContainer kieContainer 
  = kieServices.newKieContainer(krDefaultReleaseId);

4.6 KieSession

KieContainer 获取 KieSession。应用程序通过 KieSession 交互,存储并执行运行时数据:

KieSession kieSession = kieContainer.newKieSession();

完整配置如下:

public KieSession getKieSession() {
    KieBuilder kb = kieServices.newKieBuilder(getKieFileSystem());
    kb.buildAll();

    KieRepository kieRepository = kieServices.getRepository();
    ReleaseId krDefaultReleaseId = kieRepository.getDefaultReleaseId();
    KieContainer kieContainer = kieServices.newKieContainer(krDefaultReleaseId);

    return kieContainer.newKieSession();
}

5. 实现规则

完成配置后,我们来看两种创建规则的方式。通过一个示例探索规则实现:根据当前薪资和工作经验年限,为特定角色对申请人进行分类。

5.1 Drools 规则文件 (.drl)

简单说,Drools 规则文件包含所有业务规则。规则包含 When-Then 结构When 部分列出待检查的条件,Then 部分列出条件满足时执行的操作:

package com.baeldung.drools.rules;

import com.baeldung.drools.model.Applicant;

global com.baeldung.drools.model.SuggestedRole suggestedRole;

dialect  "mvel"

rule "Suggest Manager Role"
    when
        Applicant(experienceInYears > 10)
        Applicant(currentSalary > 1000000 && currentSalary <= 
         2500000)
    then
        suggestedRole.setRole("Manager");
end

通过在 KieSession 中插入 ApplicantSuggestedRole 事实触发规则:

public SuggestedRole suggestARoleForApplicant(
    Applicant applicant,SuggestedRole suggestedRole){
    KieSession kieSession = kieContainer.newKieSession();
    kieSession.insert(applicant);
    kieSession.setGlobal("suggestedRole",suggestedRole);
    kieSession.fireAllRules();
    // ...
}

该规则测试 Applicant 实例的两个条件,根据条件满足情况在 SuggestedRole 对象中设置 Role 字段。

通过执行测试验证:

@Test
public void whenCriteriaMatching_ThenSuggestManagerRole(){
    Applicant applicant = new Applicant("David", 37, 1600000.0,11);
    SuggestedRole suggestedRole = new SuggestedRole();
        
    applicantService.suggestARoleForApplicant(applicant, suggestedRole);
 
    assertEquals("Manager", suggestedRole.getRole());
}

示例中使用的 Drools 关键字说明:

  • package:指定 kmodule.xml 中的包名,规则文件位于此包内
  • import:类似 Java 的 import 语句,需指定插入 KnowledgeSession 的类
  • global:定义会话级别的全局变量,用于传递输入参数或获取输出参数
  • dialect:指定条件或操作部分表达式的语法,默认为 Java。Drools 还支持 mvel(Java 应用的表达式语言),支持字段和方法/getter 访问
  • rule:定义包含规则名的规则块
  • when:指定规则条件,示例中检查 ApplicantexperienceInYears 是否大于十年且 currentSalary 在特定范围
  • then:当 when 块条件满足时执行操作,示例中将 Applicant 角色设为 Manager

5.2 决策表

决策表提供在预格式化 Excel 电子表格中定义规则的能力。Drools 决策表的优势在于非技术人员也能轻松理解。

当存在相似规则但值不同时特别有用——此时在 Excel 表中添加新行比在 .drl 文件中编写新规则更简单。下面根据产品类型应用标签的示例展示决策表结构:

Screen-Shot-2017-04-26-at-12.26.59-PM-2

决策表分为不同部分:

  • 顶部是标题部分,指定 RuleSet(规则文件所在包)、Import(需导入的 Java 类)和 Notes(规则用途注释)
  • 定义规则的核心部分称为 RuleTable,它将应用于同一领域对象的规则分组

后续行包含列类型 CONDITIONACTION。在这些列中,可访问一行中提到的领域对象属性及其在后续行中的值。

触发规则的机制与 .drl 文件相同。

通过执行测试验证规则应用结果:

@Test
public void whenProductTypeElectronic_ThenLabelBarcode() {
    Product product = new Product("Microwave", "Electronic");
    product = productService.applyLabelToProduct(product);
    
    assertEquals("BarCode", product.getLabel());
}

6. 总结

本文快速探索了在应用程序中使用 Drools 作为业务规则引擎的方法。我们还看到了多种编写规则的方式:既可使用 Drools 规则语言,也可在电子表格中使用易于理解的语言定义规则。

本文完整代码可在 GitHub 获取。


原始标题:Introduction to Drools