Spring Boot控制器调用RuleUnitProvider.get()报错,无法找到规则单元
问题描述
我在Spring Boot项目中引入了Drools RuleUnit依赖:
<dependency> <groupId>org.drools</groupId> <artifactId>drools-ruleunits-engine</artifactId> <version>8.33.0.Final</version> </dependency>
通过RuleUnit配置规则后,单元测试中调用RuleUnitProvider.get().createRuleUnitInstance(requestAccessUnit);可以正常创建实例,但在@RequestController中执行相同代码时抛出错误:
java.lang.RuntimeException: Cannot find any rule unit for RuleUnitData of class:yyyy.xxxx.zzzz.RuleUnit
日志同时显示KieProject - No files found for KieBase defaultKieBase,推测规则文件未被加载到KieBase中。
想确认是否必须使用kie-maven-plugin并打包为kjar才能解决?有没有不使用kjar的方案?我们的构建架构引入kjar会非常麻烦,官方Spring Boot集成RuleUnit的文档很少,在Kogito中也遇到了相同问题。
不需要强制使用kjar,以下是可行的替代方案
1. 手动构建KieContainer并指定规则文件
Drools默认会在kjar结构中自动扫描规则文件,但普通Spring Boot jar中需要手动构建KieContainer,明确指定规则文件路径。
步骤1:创建Drools配置类,初始化KieContainer Bean
import org.kie.api.KieServices; import org.kie.api.builder.KieBuilder; import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.KieModule; import org.kie.api.builder.KieRepository; import org.kie.api.runtime.KieContainer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class DroolsConfig { @Bean public KieContainer kieContainer() { KieServices kieServices = KieServices.Factory.get(); KieFileSystem kieFileSystem = kieServices.newKieFileSystem(); // 添加规则文件,替换为你的实际规则文件路径 // 示例:规则文件放在src/main/resources/rules目录下 kieFileSystem.write("src/main/resources/rules/AccessControlRules.drl"); // 多个规则文件可多次调用write方法 KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem); kieBuilder.buildAll(); // 编译规则 KieRepository kieRepository = kieServices.getRepository(); KieModule kieModule = kieRepository.getKieModule(kieRepository.getDefaultReleaseId()); return kieServices.newKieContainer(kieModule.getReleaseId()); } }
步骤2:在Controller中注入KieContainer,关联RuleUnit
import org.kie.api.runtime.rule.RuleUnitInstance; import org.kie.api.runtime.rule.RuleUnitProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class AccessControlController { @Autowired private KieContainer kieContainer; @PostMapping("/check-access") public AccessResponse checkAccess(@RequestBody AccessRequest request) { // 初始化RuleUnit数据对象 RequestAccessUnit ruleUnitData = new RequestAccessUnit(); ruleUnitData.setRequest(request); // 使用指定的KieContainer创建RuleUnit实例 RuleUnitInstance<RequestAccessUnit> ruleInstance = RuleUnitProvider.get() .createRuleUnitInstance(kieContainer, ruleUnitData); ruleInstance.fire(); // 执行规则 ruleInstance.close(); return ruleUnitData.getResponse(); } }
2. 确保规则文件被正确打包到Spring Boot Jar中
检查pom.xml的资源配置,确保规则文件(.drl、.rule等后缀)被包含在最终jar里:
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.drl</include> <include>**/*.rule</include> <include>**/*.dslr</include> </includes> </resource> </resources> </build>
3. (可选)使用kmodule.xml简化规则扫描
如果不想手动逐个添加规则文件,可以在src/main/resources/META-INF下创建kmodule.xml,定义KieBase和KieSession的扫描路径:
<?xml version="1.0" encoding="UTF-8"?> <kmodule xmlns="http://www.drools.org/xsd/kmodule"> <kbase name="defaultKieBase" packages="rules.*"> <ksession name="defaultKieSession"/> </kbase> </kmodule>
这里packages="rules.*"表示扫描src/main/resources/rules目录下的所有规则文件,此时Drools会自动加载这些文件,无需在配置类中手动写入路径。
为什么单元测试正常但Controller中报错?
单元测试环境中,类路径直接包含了src/main/resources下的规则文件,Drools默认扫描机制可以找到文件;但Spring Boot打包后,默认的KieBase扫描逻辑不会自动识别普通jar中的规则文件,必须手动指定加载路径或通过kmodule.xml配置扫描规则。
内容的提问来源于stack exchange,提问作者Eugene Shvartsman

