Drools(BRMS) 速成教程(上)


大家在日常開發中,肯定遇到過一些業務規則變來變去的需求,比如:會員積分系統(今天要新注冊會員送10積分,明天要改成注冊送優惠券,后天搞活動要改成注冊自動變成高級會員...),此類需求,一般都是通過寫if分支來實現的,參考下面:

if (規則條件1){
   //處理1
}
else if (規則條件2){
   //處理2
}
else if (規則條件3){
   //處理3
}
...

這種代碼毫無營養,而且很枯燥,有沒有辦法,將業務規則從代碼中抽離出來,以后規則變了,不用改代碼,只改規則配置就行?

今天要介紹的Drools,可以很好的解決此類問題,Drools是一個業務規則管理的開源框架,現在歸到jboss旗下,本文將介紹一些基本的用法,方便大家快速上手。

一、添加依賴項

    <properties>
        <drools.version>6.5.0.Final</drools.version>
        <lombok.version>1.18.2</lombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>${drools.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>

        </dependency>
    </dependencies>

注:不同版本的drools,api有較大差異,本文采用6.5.0.Final版本,其它版本的用法請自行參考官方文檔。(lombok是可選的,建議加上,簡化java代碼書寫)

 

二、新建一個演示用的pojo類Message

package com.cnblogs.yjmyzz.drools.demo.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
@AllArgsConstructor
public class Message {

    public enum MessageType {
        HI,
        GOODBYE,
        CHAT
    }

    private MessageType messageType;
    private String target;

}  

很簡單,不用多說,我們要模擬的場景是針對不同的messageType及target(也就是不同的業務規則 ),代碼能做出不同的處理。

 

三、編寫業務規則drl文件

drl 是drools rule的縮寫,大概長這個樣子:(規則文件一般放在resources資源目錄或下面的子目錄中),將下面的內容保存在hello.drl中

package com.cnblogs.yjmyzz.drools;

import com.cnblogs.yjmyzz.drools.demo.model.Message;
import java.util.concurrent.atomic.AtomicInteger;

global String temp;
global AtomicInteger count;

//函數示例
function void print(String messgae){
    System.out.println(messgae);
}

//規則1
rule "say-hi"
    when
        $message: Message(Message.MessageType.HI.equals(messageType) && target!=null)
    then
        print("hi," + $message.getTarget() + ", welcome to drools\n");
    end

這里面可以分成幾個部分:

3.1 package部分

這個是用來管理包的,跟java的package概念類似,多個rule文件時,可以按包來管理rule代碼。

3.2 import

drl 規則文件中,可以直接使用java定義好的類,只需要import進來就好。

3.3 global

相當於全局變量聲明,多個規則文件中可共享該變量(后面會演示這一用法),要注意的是:共享全局變量建議不要用Integer這種"簡單"類型,這樣無法在規則文件中修改變量的"值",建議用復雜類型(比如上面的AtomicInteger)

3.4 function

即:函數,可以定義一些共用函數,在本drl文件被其它規則共用。

3.5 rule ... when ... then ... end

這個就是真正的規則了,rule后面的"say-hi"為規則名稱,when后面的相當於判斷條件(注:聲明條件的同時,還能聲明所謂fact"變量"-[不太准確,暫且這樣叫吧],$message: Message(...) 這里就相當於把后面一串東西,保存在$message這個fact"變量中)

小結一下:上面這個規則,相當於,如果Message的實例,其messageType為HI,且target值不為空,就打印輸出一句話。

很簡單吧,我們再加點難度,多加幾個規則 :

package com.cnblogs.yjmyzz.drools;

import com.cnblogs.yjmyzz.drools.demo.model.Message;
import java.util.concurrent.atomic.AtomicInteger;

global String temp;
global AtomicInteger count;

//函數示例
function void print(String messgae){
    System.out.println(messgae);
}

//規則1
rule "say-hi"
    when
        $message: Message(Message.MessageType.HI.equals(messageType) && target!=null)
    then
        print("hi," + $message.getTarget() + ", welcome to drools\n");
    end

rule "say-goodbye"
    when
        $message: Message(Message.MessageType.GOODBYE.equals(messageType) && target!=null)
    then
        print("bye bye ," + $message.getTarget() + "\n");
    end

rule "chat-and-goodbye"
    when
        $message: Message(Message.MessageType.CHAT.equals(messageType) && target!=null)
    then
        print($message.getTarget() +  ", nice to meet you. But I have to go.");
        //將MessageType設置成GOODBYE
        $message.setMessageType(Message.MessageType.GOODBYE);
        //更新fact,以便觸發規則"say-goodbye"
        update($message);
    end

rule "give-me-money"
    salience -1 //規則觸發的優先級,值越大,越先觸發
    when
        $message: Message(target.equals("beggar"))
    then
        print("5毛拿好");
    end

rule "give-me-rice"
    salience 1
    when
        $message: Message(target.equals("beggar"))
    then
        print("給你個包子吧");
    end

//本規則的效果:如果target="loop",會循環觸發,真到10次后停下
rule "loop"
//    no-loop  //加上這行后,將禁止循環觸發
    when
        $message: Message(target.equals("loop") && count.get()<10)
    then
        print("\n我會每隔1秒觸發,10次后停止!" + count.addAndGet(1));
        Thread.sleep(1000);
        update($message)
    end

解釋下:

a: "chat-and-goodbye" 這條規則,如果messageType=CHAT,會修改$message.messageType為GOODBYE,然后update($mesage),相當於修改了Message實例后,會重新匹配say-goodbye規則

b:"give-me-money"、"give-me-rice" 這二個規則設置了salience,其實就是優先級,值越大,該規則越優先匹配。

c: "loop" 最后一條規則,這里用到了一個全局變量count,每次該規則且匹配到以后,計數器+1,然后再update,又匹配到本條規則,最終規則就是循環觸發10次。

 

一個項目里,可以同時有多個規則文件,還可以再加一個hello2.drl,演示共享變量

package com.cnblogs.yjmyzz.drools;

import com.cnblogs.yjmyzz.drools.demo.model.Message;

rule "global-demo"
    salience -99
    when
        $message: Message(target.equals("beggar"))
    then
        System.out.println(temp);
    end

這里打印了共享變量temp(前提是target="begger")

 

四、resource/META-INF里放置kmodule.xml文件

內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="hello" packages="hello">
        <ksession name="ksession-hello"/>
    </kbase>
</kmodule>

這個文件的主要作用之一,是在運行時,讓drools知道加載哪些drl文件。注意:這里packages="hello",就表示加載classpath:resources/hello下的drl文件。

最后項目的文件結構類似這樣:

 

五、跑一把

HelloApp內容如下:

package com.cnblogs.yjmyzz.drools.demo;

import com.cnblogs.yjmyzz.drools.demo.model.Message;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

import java.util.concurrent.atomic.AtomicInteger;

public class HelloApp {


    public static void main(String[] args) {
        KieContainer kContainer = null;
        try {
            KieServices ks = KieServices.Factory.get();
            kContainer = ks.getKieClasspathContainer();
            KieSession kSession = kContainer.newKieSession("ksession-hello");

            Message message1 = new Message(Message.MessageType.HI, "楊過");
            kSession.insert(message1);
            kSession.fireAllRules();

            Message message2 = new Message(Message.MessageType.GOODBYE, "姑姑");
            kSession.insert(message2);
            kSession.fireAllRules();

            Message message3 = new Message(Message.MessageType.CHAT, "美羊羊");
            kSession.insert(message3);
            kSession.fireAllRules();

            Message message4 = new Message(null, "beggar");
            kSession.setGlobal("temp", "我是誰?我在哪?我要干什么?");
            kSession.insert(message4);
            kSession.fireAllRules();

            Message message5 = new Message(null, "loop");
            kSession.setGlobal("count", new AtomicInteger(0));
            kSession.insert(message5);
            kSession.fireAllRules();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (kContainer != null) {
                kContainer.dispose();
            }
        }

    }
}

注意下共享變量,即:message4,message5部分,一般是在規則觸發前提前把共享變量先設置好初始值,最終輸出如下:

hi,楊過, welcome to drools //規則:say-hi

bye bye ,姑姑 //規則:say-goodbye

美羊羊, nice to meet you. But I have to go. //規則:chat-and-goodbye
bye bye ,美羊羊 //規則: say-goodbye(2次匹配成功)

給你個包子吧 //規則:give-me-rice
5毛拿好 //規則:give-me-money
我是誰?我在哪?我要干什么?//hello2.drl中的規則"global-demo"

我會每隔1秒觸發,10次后停止!1 //規則:loop循環10次

我會每隔1秒觸發,10次后停止!2

我會每隔1秒觸發,10次后停止!3

我會每隔1秒觸發,10次后停止!4

我會每隔1秒觸發,10次后停止!5

我會每隔1秒觸發,10次后停止!6

我會每隔1秒觸發,10次后停止!7

我會每隔1秒觸發,10次后停止!8

我會每隔1秒觸發,10次后停止!9

我會每隔1秒觸發,10次后停止!10

  

參考文章:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM