Drools規則引擎入門指南(一)

最近項目需要增加風控系統,在經過一番調研以後決定使用Drools規則引擎。因爲項目是基於SpringCloud的架構,所以此次學習使用了SpringBoot2.0版本結合Drools7.14.0.Final版本。

引入依賴

1
2
3
4
5
6
7
8
9
10
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.14.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>7.14.0.Final</version>
</dependency>

創建配置類

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Configuration
public class DroolsAutoConfiguration {
    private static final String RULES_PATH = "rules/";

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
    }

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        final KieRepository kieRepository = getKieServices().getRepository();
        kieRepository.addKieModule(new KieModule() {
            public ReleaseId getReleaseId() {
                return kieRepository.getDefaultReleaseId();
            }
        });
        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return getKieServices().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();
        return kieSession;
    }

    @Bean
    @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
    public KieServices getKieServices() {
        System.setProperty("drools.dateformat","yyyy-MM-dd");
        return KieServices.Factory.get();
    }
}

在這個時候我們的基本環境已經搭建好了,接下來我們一起來學習Drools吧

HelloWord

程序員的世界裏,我們學習任何一門語言都是以HelloWord開啓的,本次學習也不例外。

1.創建規則文件

Drools的規則文件是以*.drl結尾的文件,我們來看一個最簡單的規則文件中都是包含什麼。

通常來說,我們會把規則文件放在resources資源文件夾下,這裏呢我們在resources文件夾下新建一個rules文件夾,然後再新建一個HelloWord.drl文件

1
2
3
4
5
6
7
8
9
10
11
package rules;
import cn.org.zhixiang.entity.User;
import java.lang.String;
import java.util.List;

rule "hello,word"
    when
        eval(true)
    then
        System.err.println("hello,word!");
end
  1. 規則文件,就是我們新建的這個HelloWord.drl可以理解爲一個Java類
  2. package,這個跟Java中的包名是差不多的
  3. import,此文件中需要的類。
  4. rule,可以理解爲給這個規則起的一個名字,一個規則文件中可以包含多個rule。
  5. when,when下面可以放置一些條件判斷的表達式以及定義一些變量什麼的。如果裏面內容爲空的話則會默認添加一個eval(true)代表一個爲true的表達式
  6. then,當when下面的表達式爲true是then下方的代碼纔會執行,在這裏可以直接編寫Java代碼(代碼所需要的類通過import引入),當然也可以使用when模塊定義的一些變量
  7. end 代表規則hello,word的結束。

2.Java調用

現在我們的規則文件寫好以後就可以在Java中來進行調用了。

1. 新建一個測試類DroolsApplicationHelloWordTests

1
2
3
4
5
6
7
8
@RunWith(SpringRunner.class)
@SpringBootTest
public class DroolsApplicationHelloWordTests {

    @Autowired
    KieSession kieSession;

}

上方注入的kieSession對象就是以後與Drools打交道最常用的一個對象了,通過它可以直接操作在配置類kieFileSystem方法中加載的所有的規則文件

2. 編寫測試代碼

1
2
3
4
@Test
public void testHelloWord() {
    kieSession.fireAllRules();
}

kieSession.fireAllRules方法是執行所有的規則,在運行了這個測試方法之後我們應該就可以看到控制檯打印的一句hello,word!了

基礎學習

1. 向規則文件傳參

1. 在entity包下新增一個User的實體類

1
2
3
4
5
6
7
8
9
10
public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //省略getter,setter
}

2. 在hello,word規則下方再次新建一個規則user

1
2
3
4
5
6
7
rule "user"
    when
      $u:User(name=="張三",age==18)
    then
        $u.setName("李四");
        System.err.println("hello,word:"+$u.getName());
end

$u:User(name==“張三”,age==18)的意思就是當存在一個user對象,並且它的name屬性等於張三age等於18時就把這個對象賦值給$u。

在下方的then模塊,如果上方的條件成立時就把$u的name屬性更新一下,然後打印。

3. 編寫測試代碼

1
2
3
4
5
6
7
@Test
public void testUser() {
    User user=new User("張三",18);
    kieSession.insert(user);
    kieSession.fireAllRules();
    System.err.println("規則執行完畢後張三變爲了:"+user.getName());
}

我們可以使用kieSession.insert方法向規則文件中傳參,然後在調用方法後你會發現在規則文件中更改的值在Java代碼中也被更改了。

4. 存在的小問題

可能你會發現上方代碼執行的時候連那句helloword也打印了,爲什麼呢,這是因爲HelloWord那條規則沒有驗證條件再加上kieSession.fireAllRules()本來就是執行所有被加載的規則的。那麼避免這種情況的辦法就是執定本次執行的規則

1
2
3
4
5
6
@Test
    public void testOneRule() {
        User user=new User("張三",18);
        kieSession.insert(user);
        kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("user"));
    }

上方的user就是指定的本次執行的規則名稱了。

5.擴展操作

上方我們通過RuleNameEndsWithAgendaFilter對象成功指定了需要執行的規則文件,其實通過查看此對象的源碼我們發現這個對象是AgendaFilter的一個實現類,決定執不執行一個規則的條件是accept方法返回的boolean值決定的。
所以說如果我們希望可以一次批量匹配多個規則的話可以通過繼承AgendaFilter重寫accept方法哦

2. 常用運算符

1. 連接符

Drools中存在的三種連接符,上方的代碼中我們已經使用過一個了,那就$u:User(name==“張三”,age==18)中的逗號,這裏的逗號其實就是and的意思。另外的兩個運算符就是&&和||,相信它們兩個的意思不用我來介紹了吧。

不過有一點需要注意的是&&和|| 和逗號,不能同時出現。要不你選擇用&&和||要不就只用逗號, 。

2. 類型比較操作符

1.首先就是<,>,==,!=,>=,<=這六個

它們是配合eval使用的,比如上方我們使用的eval(true)就是直接返回的true。當我們比較常量時可以使用eval(u.age>b.age)

2. contains not contains

contains用於判斷對象的某個字段是否包含另外一個對象

1
2
3
4
5
6
7
rule "contains"
    when
      $s:String()
      $u:User(name contains $s)
    then
        System.err.println("用戶張三存在");
end
1
2
3
4
5
6
7
8
@Test
    public void testContains() {
       String name="張三";
        User user=new User("張三",18);
        kieSession.insert(name);
        kieSession.insert(user);
        kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("contains"));
    }

not contains顧明思議就是不包含

3. memberOf not memberOf

memberOf用於判斷對象的某個字段是否存在一個集合中

1
2
3
4
5
6
7
rule "memberOf"
    when
      $list:List()
      $u:User(name memberOf $list)
    then
        System.err.println("用戶李四存在");
end
1
2
3
4
5
6
7
8
9
10
@Test
    public void testMemberOf() {
        List list=new ArrayList();
        list.add("張三");
        list.add("李四");
        User user=new User("李四",18);
        kieSession.insert(list);
        kieSession.insert(user);
        kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("memberOf"));
    }

not memberOf顧明思議就是不存在

3. matches not matches

matches就是用於匹配正則表達式的了

1
2
3
4
5
6
rule "matches"
    when
      $u:User(name matches "張.*")
    then
        System.err.println("用戶張xx存在");
end
1
2
3
4
5
6
@Test
   public void testMatches() {
       User user=new User("張三",18);
       kieSession.insert(user);
       kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("matches"));
   }

not matches不用我說了吧

本文所有源碼:https://github.com/shiyujun/drools

本文出自http://zhixiang.org.cn/,轉載請保留

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章