Springboot集成规则引擎Drools方式
作者: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下的。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。