場(chǎng)景描述:
需要判斷的規(guī)則有上千上萬(wàn)個(gè)或者更多。
舉個(gè)例子:法律案件或者交通法規(guī),像這類場(chǎng)景柬批,每一個(gè)法條都相當(dāng)于是一個(gè)條件啸澡。(如:是什么原因?qū)е逻`章的?是闖了紅燈啊氮帐,還是超速了呀還是超載了呀等等等)各種if-else或者switch-case判斷嗅虏。
這些是已有的規(guī)則判斷,好說(shuō)上沐,弄個(gè)Excel表格或者插入到庫(kù)中皮服,一次性讀出來(lái),用kie提供的方式直接用就行了参咙。但是如果后期有新增怎么辦龄广?
本文只分享新增的場(chǎng)景解決的方案。更新和刪除部分目前還沒(méi)找解決方法蕴侧。
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
? ? ? ? xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
? ? <modelVersion>4.0.0</modelVersion>
? ? <parent>
? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? <artifactId>spring-boot-starter-parent</artifactId>
? ? ? ? <version>2.2.5.RELEASE</version>
? ? ? ? <relativePath/> <!-- lookup parent from repository -->
? ? </parent>
? ? <groupId>com.***.drools</groupId>
? ? <artifactId>rule-engine</artifactId>
? ? <version>1.0.0</version>
? ? <name>rule-engine</name>
? ? <description>Demo project for Spring Boot</description>
? ? <properties>
? ? ? ? <java.version>1.8</java.version>
? ? ? ? <drools.version>7.33.0.Final</drools.version>
? ? </properties>
? ? <dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>javax.inject</groupId>
? ? ? ? ? ? <artifactId>javax.inject</artifactId>
? ? ? ? ? ? <version>1</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-web</artifactId>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.mybatis.spring.boot</groupId>
? ? ? ? ? ? <artifactId>mybatis-spring-boot-starter</artifactId>
? ? ? ? ? ? <version>2.1.2</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>mysql</groupId>
? ? ? ? ? ? <artifactId>mysql-connector-java</artifactId>
? ? ? ? ? ? <version>8.0.19</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.projectlombok</groupId>
? ? ? ? ? ? <artifactId>lombok</artifactId>
? ? ? ? ? ? <optional>true</optional>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId>
? ? ? ? ? ? <scope>test</scope>
? ? ? ? ? ? <exclusions>
? ? ? ? ? ? ? ? <exclusion>
? ? ? ? ? ? ? ? ? ? <groupId>org.junit.vintage</groupId>
? ? ? ? ? ? ? ? ? ? <artifactId>junit-vintage-engine</artifactId>
? ? ? ? ? ? ? ? </exclusion>
? ? ? ? ? ? </exclusions>
? ? ? ? </dependency>
? ? ? ? <!--kie-->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.kie</groupId>
? ? ? ? ? ? <artifactId>kie-api</artifactId>
? ? ? ? ? ? <version>${drools.version}</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.drools</groupId>
? ? ? ? ? ? <artifactId>drools-core</artifactId>
? ? ? ? ? ? <version>${drools.version}</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.drools</groupId>
? ? ? ? ? ? <artifactId>drools-compiler</artifactId>
? ? ? ? ? ? <version>${drools.version}</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.drools</groupId>
? ? ? ? ? ? <artifactId>drools-decisiontables</artifactId>
? ? ? ? ? ? <version>${drools.version}</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.drools</groupId>
? ? ? ? ? ? <artifactId>drools-templates</artifactId>
? ? ? ? ? ? <version>${drools.version}</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.kie</groupId>
? ? ? ? ? ? <artifactId>kie-internal</artifactId>
? ? ? ? ? ? <version>${drools.version}</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.mvel</groupId>
? ? ? ? ? ? <artifactId>mvel2</artifactId>
? ? ? ? ? ? <version>2.4.4.Final</version>
? ? ? ? </dependency>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>com.thoughtworks.xstream</groupId>
? ? ? ? ? ? <artifactId>xstream</artifactId>
? ? ? ? ? ? <version>1.4.11.1</version>
? ? ? ? </dependency>
? ? ? ? <!-- Logging -->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.slf4j</groupId>
? ? ? ? ? ? <artifactId>slf4j-api</artifactId>
? ? ? ? </dependency>
? ? </dependencies>
? ? <build>
? ? ? ? <plugins>
? ? ? ? ? ? <plugin>
? ? ? ? ? ? ? ? <groupId>org.springframework.boot</groupId>
? ? ? ? ? ? ? ? <artifactId>spring-boot-maven-plugin</artifactId>
? ? ? ? ? ? </plugin>
? ? ? ? </plugins>
? ? </build>
</project>
實(shí)現(xiàn)思路:
1. 使用KieHelper來(lái)build()择同。
故此KieHelper要注冊(cè)成一個(gè)Bean。能保持單利净宵,保證后期動(dòng)態(tài)編譯的時(shí)候不會(huì)丟失已編譯過(guò)的規(guī)則敲才。?
@Bean
public KieHelper kieHelper() {
? ? return new KieHelper();
}
DataObject-實(shí)體封裝類
@Data
public class DataObject {
? ? private String ly;
}
RuleTemplate.java--規(guī)則封裝類
public class RuleTemplate {
? ? private String template;
? ? public RuleTemplate(String template) {
? ? ? ? this.template = template;
? ? }
? ? public String getTemplate() {
? ? ? ? return template;
? ? }
? ? public void setTemplate(String template) {
? ? ? ? this.template = template;
? ? }
}
RuleTemplate的構(gòu)造器里的參數(shù)其實(shí)就是Drl文件的字符串形式。根據(jù)自己的業(yè)務(wù)去拼择葡。我這塊是直接去讀的庫(kù)紧武。
@Bean
public RuleTemplate ruleTemplate() {
? ? RuleTemplate ruleTemplate = new RuleTemplate(CreateRule.getInstance().ruleTemplate(IRulesService.queryAll()));
? ? return ruleTemplate;
}
2. 容器初始化的時(shí)候去讀取已有的規(guī)則數(shù)據(jù)(數(shù)據(jù)庫(kù)或者Excel都行),我是直接讀的庫(kù)。
? ? 2.1 查完拼接成規(guī)則字符串(拼完后可以弄到drl文件里試試規(guī)則生成的是否有問(wèn)題)敏储。
? ? 2.2 使用kieHelper來(lái)添加已經(jīng)拼好的rule字符串阻星。然后在build。
@Bean
@PostConstruct
public KieBase kieBase() {
? ? //動(dòng)態(tài)加載的核心實(shí)現(xiàn)方式
? ? kieHelper.addContent(ruleTemplate.getTemplate(), ResourceType.DRL);//添加規(guī)則
? ? KieBase kieBase = kieHelper.build();//手動(dòng)編譯規(guī)則
? ? return kieBase;
}
到這塊是項(xiàng)目啟動(dòng)階段完成的已添,預(yù)加載已知的規(guī)則妥箕。
以下部分是項(xiàng)目運(yùn)行階段來(lái)動(dòng)態(tài)的去添加規(guī)則后編譯并查找。
1. 新規(guī)則字符串更舞。
String test = "package com.pkulaw.drools.rules;\n" +
? ? ? ? "\n" +
? ? ? ? "import com.pkulaw.drools.entity.DataObject\n" +
? ? ? ? "\n" +
? ? ? ? "global java.util.List lyList;\n" +
? ? ? ? "\n" +
? ? ? ? "rule \"ly-rule-0000\"\n" +
? ? ? ? "agenda-group \"ly-group\"\n" +
? ? ? ? "dialect \"java\"\n" +
? ? ? ? "when\n" +
? ? ? ? "? ? d : DataObject(ly == \"測(cè)試\")\n" +
? ? ? ? "then\n" +
? ? ? ? "? ? lyList.add(\"這是測(cè)試\");? ? \n" +
? ? ? ? "end";
2. KieHelper來(lái)添加和編譯畦幢。
kieHelper.addContent(ruleContent, ResourceType.DRL);
//重編譯
KieBase kieBase1 = kieHelper.build();
//重新注冊(cè)單利
beanRegister(kieBase1);
重新注冊(cè)單利的原因是每次kieHelper去build的時(shí)候會(huì)重新生成一個(gè)KieBase,而KieBase是去獲取session進(jìn)行fact匹配的關(guān)鍵疏哗。故此,要去替換Spring容器中的bean的實(shí)例禾怠。
3.1.1 替換單利bean(Deprecated)-針對(duì)Kiebase
//這個(gè)方式不夠優(yōu)雅,但也不是不可行返奉,故此留著這段。
public synchronized void beanRegister(KieBase newKBase) {
? ? DefaultListableBeanFactory factory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
? ? factory.removeBeanDefinition("kieBase");
? ? BeanDefinitionBuilder beanDefinitionBuilder =
? ? ? ? ? ? BeanDefinitionBuilder.genericBeanDefinition(newKBase.getClass().getName());
? ? // get the BeanDefinition
? ? BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
? ? factory.registerBeanDefinition("kieBase", beanDefinition);
? ? factory.registerSingleton("kieBase", newKBase);
}
3.1.2 替換KieBase
上面對(duì)直接Spring容器的bean替換不夠好吗氏,死腦筋沒(méi)轉(zhuǎn)過(guò)來(lái)芽偏。群友一提醒立馬醒悟。
public class KieBaseTemplate {
? ? private KieBase kieBase;
? ? public KieBaseTemplate(KieBase kieBase) {
? ? ? ? this.kieBase = kieBase;
? ? }
? ? public KieBase getKieBase() {
? ? ? ? return kieBase;
? ? }
? ? public void update(KieBase kieBase) {
? ? ? ? this.kieBase = kieBase;
? ? }
}
原來(lái)是Kiebase注冊(cè)成bean現(xiàn)在改成把KieBaseTemplate 注冊(cè)成bean弦讽。
@Bean
@PostConstruct
public KieBaseTemplate kieBaseTemplate() {
? ? kieHelper.addContent(ruleTemplate.getTemplate(), ResourceType.DRL);
? ? KieBase kieBase = kieHelper.build();
? ? return new KieBaseTemplate(kieBase);
}
每次新增規(guī)則后build的時(shí)候把生成的kieBase通過(guò)update的方式重新替換掉就好了污尉。
查找的時(shí)候通過(guò)kieBaseTemplate的getKieBase方法獲取KieBase膀哲。
(有時(shí)候換個(gè)思路去思考能寫(xiě)出更優(yōu)雅的代碼來(lái),這一段就比直接操作Bean優(yōu)雅多了)
以下是查找測(cè)試代碼
KieSession kSession = kieBaseTemplate.getKieBase().newKieSession();
DataObject obj1 = new DataObject();
obj1.setLy("測(cè)試");
List lyList = new ArrayList();
kSession.setGlobal("lyList", lyList);
kSession.insert(obj1);
kSession.fireAllRules();
System.out.println(lyList);
總結(jié)
?這段時(shí)間由于公司需求去研究一下這個(gè)工具被碗,基礎(chǔ)知識(shí)閱讀官方文檔即可某宪,實(shí)現(xiàn)方式有多重,官網(wǎng)介紹有InternalKnowledgeBase來(lái)實(shí)現(xiàn)的锐朴,還有一種是KieBuilder來(lái)實(shí)現(xiàn)的兴喂。KieHelper是我在GitHub(他的地址)上借鑒某位大佬的實(shí)現(xiàn)方式所得到的啟發(fā)。
歡迎評(píng)論和批評(píng)焚志,菜鳥(niǎo)一枚衣迷,有錯(cuò)誤及時(shí)修正,以免誤導(dǎo)他人酱酬。