Springboot集成規(guī)則引擎Drools方式
Drools 是用 Java 語言編寫的開放源碼規(guī)則引擎,使用 Rete 算法對所編寫的規(guī)則求值。
Drools 允許使用聲明方式表達(dá)業(yè)務(wù)邏輯。
可以使用非 XML 的本地語言編寫規(guī)則,從而便于學(xué)習(xí)和理解。
并且,還可以將 Java 代碼直接嵌入到規(guī)則文件中。
詳細(xì)可見開源業(yè)務(wù)規(guī)則引擎:Drools中文網(wǎng)
一、項(xiàng)目目錄結(jié)構(gòu)

二、集成drools
1、引入依賴
<!--drools規(guī)則引擎-->
<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、新建規(guī)則文件
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("參數(shù):getParamId="+queryParam.getParamId()+";getParamSign="+queryParam.getParamSign());
endrule2.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() + "是女孩");
end3、創(chuàng)建KieUtils(因要做熱加載,需要重載規(guī)則文件,規(guī)則引擎容器要支持變動(dòng))
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的一個(gè)注解, 主要實(shí)現(xiàn)的是,當(dāng)你的bean被注冊之后,如果而注冊相同類型的bean,就不會(huì)成功,
* 它會(huì)保證你的bean只有一個(gè),即你的實(shí)例只有一個(gè),當(dāng)你注冊多個(gè)相同的bean時(shí),會(huì)出現(xiàn)異常
*/
@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
//獲取初始化規(guī)則文件所在路徑
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);
//將規(guī)則文件寫規(guī)則引擎系統(tǒng)內(nèi)
kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
}
return kieFileSystem;
}
/**
* 創(chuàng)建KIE內(nèi)部容器
*
* @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、重載實(shí)現(xiàn),可通過鏈接數(shù)據(jù)庫刷新規(guī)則
@Slf4j
@Service
public class ReloadDroolsRules {
@Autowired
private KieSession kieSession;
@Autowired
private KieContainer kieContainer;
/**
* 重新加載規(guī)則
* @param drlName 規(guī)則名稱
* @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("新規(guī)則重載成功");
}
/**
* 重新讀取數(shù)據(jù)庫配置內(nèi)容
* @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";
// 從數(shù)據(jù)庫加載的規(guī)則
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加載的規(guī)則
//獲取初始化規(guī)則文件所在路徑
String path = null;
for (Resource file : getRuleFiles(drlName)) {
path = RuleEngineConfig.RULES_PATH + file.getFilename();
log.info("path=" + path);
//將規(guī)則文件寫規(guī)則引擎系統(tǒng)內(nèi)
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 + ".*");
}
}三、驗(yàn)證測試
1、創(chuàng)建測試實(shí)體類
@Data
public class QueryParam {
private String paramId;
private String paramSign;
}2、添加規(guī)則實(shí)現(xiàn)
@Slf4j
@Service
public class RuleEngineService {
/**
* 插入規(guī)則
*
* @param param
*/
public void executeAddRule(QueryParam param) {
log.info("參數(shù)數(shù)據(jù):" + param.getParamId() + ";" + param.getParamSign());
log.info("插入規(guī)則");
}
/**
* 移除規(guī)則
*
* @param param
*/
public void executeRemoveRule(QueryParam param) {
log.info("參數(shù)數(shù)據(jù):" + param.getParamId() + ";" + param.getParamSign());
log.info("移除規(guī)則");
}
}3、API調(diào)用實(shí)現(xiàn)
@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("-");
// 入?yún)?
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 "新規(guī)則重載成功";
}
}運(yùn)行服務(wù),查看結(jié)果
調(diào)用 localhost:9666/api/v1/test/param

修改規(guī)則文件,調(diào)用重新加載配置 localhost:9666/api/v1/test/reload,不指定配置規(guī)則文件名,默認(rèn)重新加載全部規(guī)則,然后重新調(diào)用請求

需要注意點(diǎn):
修改規(guī)則配置,是修改加載后的文件,也就是 運(yùn)行項(xiàng)目時(shí),生成的 target目錄下加載的規(guī)則文件,而不是項(xiàng)目本身resources下的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot集成Drools打造動(dòng)態(tài)規(guī)則管理模板引擎
- Spring Boot 中使用 Drools 規(guī)則引擎的完整步驟
- Spring Boot整合Drools規(guī)則引擎實(shí)戰(zhàn)指南及最佳實(shí)踐
- SpringBoot整合Drools規(guī)則引擎動(dòng)態(tài)生成業(yè)務(wù)規(guī)則的實(shí)現(xiàn)
- SpringBoot2整合Drools規(guī)則引擎及案例詳解
- Spring Boot+Drools規(guī)則引擎整合詳解
- SpringBoot集成Drools規(guī)則引擎的實(shí)現(xiàn)實(shí)例
相關(guān)文章
MyBatis實(shí)現(xiàn)動(dòng)態(tài)查詢、模糊查詢功能
這篇文章主要介紹了MyBatis實(shí)現(xiàn)動(dòng)態(tài)查詢、模糊查詢功能,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06
Java NumberFormat格式化float類型的bug
今天小編就為大家分享一篇關(guān)于Java NumberFormat格式化float類型的bug,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10
Spring?AOP的注解實(shí)現(xiàn)方式實(shí)例詳解
AOP是一種對某一類事情集中處理的思想,本文給大家介紹Spring?AOP的注解實(shí)現(xiàn)方式實(shí)例詳解,感興趣的朋友一起看看吧2025-04-04
一文徹底弄懂Java中MultipartFile接口和File類
MultipartFile是一個(gè)接口,我們可以理解為是Spring?給我們綁定的一個(gè)在使用文件上傳等時(shí)簡便實(shí)現(xiàn)的口子,這篇文章主要給大家介紹了關(guān)于如何通過一文徹底弄懂Java中MultipartFile接口和File類的相關(guān)資料,需要的朋友可以參考下2023-11-11
Java微信公眾平臺開發(fā)(2) 微信服務(wù)器post消息體的接收
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺開發(fā)第二步,微信服務(wù)器post消息體的接收,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04

