一、环境概述
JDK:openjdk version "1.8.0_452"
Maven: maven 3.8.8
Drools:Drools 7.73.0.Final
SpringBoot:SpringBoot 2.7.18
二、项目依赖
这里没有引入drools整合spring依赖了,是直接手动把组件IOC的。
POM.xml
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.18</spring-boot.version>
<drools.version>7.73.0.Final</drools.version>
<lombok.version>1.18.30</lombok.version>
<junit.version>4.13.2</junit.version>
</properties>
<!-- 使用 dependencyManagement 管理 Spring Boot 和 Drools 版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-bom</artifactId>
<version>${drools.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Drools依赖 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
</dependency>
<!-- 决策表要用 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
</dependency>
<!-- SpringBoot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- 排除 JUnit 5(因为要用 JUnit 4) -->
<exclusions>
<exclusion>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 若为Web项目,添加此依赖 (二选一)-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<!-- 若为非Web项目,添加此依赖 (二选一) 这里我就不用web跑了,直接starter跑了-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- junit 4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
三、实体类和配置类
注意:这里没有用读取kmodule.xml的方式创建KieBase了,是直接代码配置类方式配置的
src/main/java/com/online/admin/entity/Person.java
package com.online.admin.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Person
{
private String name;
private int age;
}
src/main/java/com/online/admin/config/DroolsConfig.java
package com.online.admin.config;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.builder.model.KieSessionModel;
import org.kie.api.conf.EqualityBehaviorOption;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
/**
* Drools配置类
*/
@Slf4j
@Configuration
public class DroolsConfig {
@Bean
public KieServices getKieServices() {
return KieServices.Factory.get();
}
/**
* 这里就类似于读取kmodule.xml,创建模型类
*/
@Bean
public KieModuleModel kieModuleModel(KieServices kieServices) {
KieModuleModel kieModuleModel = kieServices.newKieModuleModel();
KieBaseModel kieBaseModel1 = kieModuleModel.newKieBaseModel("rule1")
.setDefault(false)
.addPackage("rule1")
.setEqualsBehavior(EqualityBehaviorOption.EQUALITY)
.setEventProcessingMode(EventProcessingOption.STREAM);
//有状态类型的Session就是规则执行过程中会话会一直存在,改变工作内存对象会再次触发规则判断,重新计算,重新输出结果。
//无状态类型的Session就要求你在开始计算的时候把所需所有事实对象全部传入,执行完计算以后会话就不存在了,只是会返回结果。
// 这里是用的有状态演示
// 有状态类型
kieBaseModel1.newKieSessionModel("ksession-rules1")
.setType(KieSessionModel.KieSessionType.STATEFUL);
KieBaseModel kieBaseModel2 = kieModuleModel.newKieBaseModel("rule2")
.setDefault(false)
.addPackage("rule2")
.setEqualsBehavior(EqualityBehaviorOption.EQUALITY)
.setEventProcessingMode(EventProcessingOption.STREAM);
// 有状态类型
kieBaseModel2.newKieSessionModel("ksession-rules2")
.setType(KieSessionModel.KieSessionType.STATEFUL);
return kieModuleModel;
}
// 这里我打算根据数据库或者配置中心读取配置创建,先读取Module配置,
// 然后根据配置读取映射的drl文件位置,根据模块区分开不同模块下的的Session。
// 这里是写死的,组件生命周期管理有问题,玩玩可以,项目上千万别学
@Bean
public KieFileSystem kieFileSystem(KieServices kieServices,KieModuleModel kieModuleModel) throws IOException {
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
kieFileSystem.writeKModuleXML(kieModuleModel.toXML());
// 1. 加载属于KieBase "rules1"的规则(路径:rule1/*.drl)
Resource[] rule1Resources = resolver.getResources("classpath*:rule1/*.drl");
for (Resource resource : rule1Resources) {
// 规则文件路径对应KieBase1的package:"rule1"
String resourcePath = "rule1/" + resource.getFilename();
kieFileSystem.write(ResourceFactory.newClassPathResource(resourcePath, "UTF-8"));
log.info("已加载KieBase(rules1)的规则文件: {}", resourcePath);
}
// 2. 加载属于KieBase "rules2"的规则(路径:rule2/*.drl)
Resource[] rule2Resources = resolver.getResources("classpath*:rule2/*.drl");
for (Resource resource : rule2Resources) {
// 规则文件路径对应KieBase2的package:"rule2"
String resourcePath = "rule2/" + resource.getFilename();
kieFileSystem.write(ResourceFactory.newClassPathResource(resourcePath, "UTF-8"));
log.info("已加载KieBase(rules2)的规则文件: {}", resourcePath);
}
return kieFileSystem;
}
@Bean
public KieContainer kieContainer(KieServices kieServices, KieFileSystem kieFileSystem, KieModuleModel kieModuleModel) {
// 构建KieModule
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
// 构建KieRepository
KieRepository kieRepository = kieServices.getRepository();
//这里我是自己玩搭建环境,用的默认的版本号,实际这里可能要自定义releaseId(规则版本号,用于区分规则版本是否发生变化用于热部署)
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
// 执行构建
kieBuilder.buildAll();
// 检查构建错误
if (kieBuilder.getResults().hasMessages(Message.Level.ERROR)) {
log.error("规则构建失败: {}", kieBuilder.getResults().getMessages());
throw new RuntimeException("规则构建失败,请检查规则文件");
}
// 创建KieContainer
return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
}
}
四、drl文件(两个内容差不多,但是包名称和位置不一样)
src/main/resources/rule1/rule1.drl
package rule1;
import com.online.admin.entity.Person;
rule "Age Check Rule"
when
$p : Person(age >= 18)
then
System.out.println("[rules1规则触发] " + $p.getName() + " 是成年人,年龄: " + $p.getAge());
end
rule "Check using eval"
when
$p : Person()
eval( $p.getAge() >= 18 )
then
System.out.println("rules1条件满足");
end
src/main/resources/rule2/rule2.drl
package rule2;
import com.online.admin.entity.Person;
rule "Age Check Rule"
when
$p : Person(age < 18)
then
System.out.println("[rules2规则触发] " + $p.getName() + " 不是成年人,年龄: " + $p.getAge());
end
rule "Check using eval"
when
$p : Person()
eval( $p.getAge() >= 18 )
then
System.out.println("rules2条件满足");
end
五、测试类
package com.online.admin;
import com.online.admin.config.DroolsConfig;
import com.online.admin.entity.Person;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Collection;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DroolsConfig.class)
public class AppTest {
@Autowired
private KieContainer kieContainer;
@Test
public void testRule1() {
KieSession kieSession = null;
try {
Collection<String> kieBaseNames = kieContainer.getKieBaseNames();
log.info("kieBaseNames : {}", kieBaseNames);
kieSession = kieContainer.newKieSession("ksession-rules1");
KieBase kieBase = kieSession.getKieBase();
log.info("kieBase : {}", kieBase.getKiePackages());
// Rule rule=kieBase.getRule("rule1","Age Check Rule");
// log.info("rule : {}", rule.getName());
Person adult = new Person("李四", 25);
kieSession.insert(adult);
int firedRules = kieSession.fireAllRules();
log.info("触发了 {} 条规则", firedRules);
} catch (RuntimeException e) {
log.error("规则执行失败", e);
Assert.fail(e.getMessage());
throw e;
} finally {
if (kieSession != null) {
try {
kieSession.dispose();
} catch (RuntimeException e) {
log.error("kiesession关闭失败,{}", e.getMessage());
}
}
}
}
@Test
public void testRule2() {
KieSession kieSession = null;
try {
kieSession = kieContainer.newKieSession("ksession-rules2");
Person adult = new Person("李四", 12);
kieSession.insert(adult);
int firedRules = kieSession.fireAllRules();
log.info("触发了 {} 条规则", firedRules);
} catch (RuntimeException e) {
log.error("规则执行失败", e);
Assert.fail(e.getMessage());
throw e;
} finally {
if (kieSession != null) {
try {
kieSession.dispose();
} catch (RuntimeException e) {
log.error("kiesession关闭失败,{}", e.getMessage());
}
}
}
}
}