對(duì)于Drools后期新增規(guī)則動(dòng)態(tài)加載的方法

場(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)他人酱酬。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壶谒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子膳沽,更是在濱河造成了極大的恐慌汗菜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贵少,死亡現(xiàn)場(chǎng)離奇詭異呵俏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)滔灶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)普碎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人录平,你說(shuō)我怎么就攤上這事麻车。” “怎么了斗这?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵动猬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我表箭,道長(zhǎng)赁咙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任免钻,我火速辦了婚禮彼水,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘极舔。我一直安慰自己凤覆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布拆魏。 她就那樣靜靜地躺著盯桦,像睡著了一般慈俯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拥峦,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天贴膘,我揣著相機(jī)與錄音,去河邊找鬼事镣。 笑死步鉴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的璃哟。 我是一名探鬼主播氛琢,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼随闪!你這毒婦竟也來(lái)了阳似?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤铐伴,失蹤者是張志新(化名)和其女友劉穎撮奏,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體当宴,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畜吊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了户矢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玲献。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖梯浪,靈堂內(nèi)的尸體忽然破棺而出捌年,到底是詐尸還是另有隱情,我是刑警寧澤挂洛,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布礼预,位于F島的核電站,受9級(jí)特大地震影響虏劲,放射性物質(zhì)發(fā)生泄漏托酸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一柒巫、第九天 我趴在偏房一處隱蔽的房頂上張望励堡。 院中可真熱鬧,春花似錦吻育、人聲如沸念秧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)摊趾。三九已至,卻和暖如春游两,著一層夾襖步出監(jiān)牢的瞬間砾层,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工贱案, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肛炮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓宝踪,卻偏偏與公主長(zhǎng)得像侨糟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘩燥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容