Springboot集成規(guī)則引擎Drools方式
Drools 是用 Java 語言編寫的開放源碼規(guī)則引擎,使用 Rete 算法對(duì)所編寫的規(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()); 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、創(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被注冊(cè)之后,如果而注冊(cè)相同類型的bean,就不會(huì)成功, * 它會(huì)保證你的bean只有一個(gè),即你的實(shí)例只有一個(gè),當(dāng)你注冊(cè)多個(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)證測(cè)試
1、創(chuàng)建測(cè)試實(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)用請(qǐng)求
需要注意點(diǎn):
修改規(guī)則配置,是修改加載后的文件,也就是 運(yùn)行項(xiàng)目時(shí),生成的 target目錄下加載的規(guī)則文件,而不是項(xiàng)目本身resources下的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java虛擬機(jī)JVM類加載機(jī)制原理(面試必問)
這篇文章主要介紹了面試當(dāng)中必會(huì)問到的java虛擬機(jī)JVM類加載機(jī)制,非常的詳細(xì),有需要的朋友可以借鑒參考下,歡迎多多交流討論2021-08-08Netty事件循環(huán)主邏輯NioEventLoop的run方法分析
這篇文章主要介紹了Netty事件循環(huán)主邏輯NioEventLoop的run方法分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03遠(yuǎn)程連接Jedis和整合SpringBoot的詳細(xì)過程
這篇文章主要介紹了遠(yuǎn)程連接Jedis和整合SpringBoot的詳細(xì)過程,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08java基于控制臺(tái)的學(xué)生學(xué)籍管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java基于控制臺(tái)的學(xué)生學(xué)籍管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07多模塊的springboot項(xiàng)目發(fā)布指定模塊的腳本方式
該文章主要介紹了如何在多模塊的SpringBoot項(xiàng)目中發(fā)布指定模塊的腳本,作者原先的腳本會(huì)清理并編譯所有模塊,導(dǎo)致發(fā)布時(shí)間過長(zhǎng),通過簡(jiǎn)化腳本,只使用`mvn clean install`命令,可以快速發(fā)布指定模塊及其依賴的模塊2025-01-01