最近項目需要增加風控系統,在經過一番調研以后決定使用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
|
- 規則文件,就是我們新建的這個HelloWord.drl可以理解為一個Java類
- package,這個跟Java中的包名是差不多的
- import,此文件中需要的類。
- rule,可以理解為給這個規則起的一個名字,一個規則文件中可以包含多個rule。
- when,when下面可以放置一些條件判斷的表達式以及定義一些變量什么的。如果里面內容為空的話則會默認添加一個eval(true)代表一個為true的表達式
- then,當when下面的表達式為true是then下方的代碼才會執行,在這里可以直接編寫Java代碼(代碼所需要的類通過import引入),當然也可以使用when模塊定義的一些變量
- 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; } |
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/,轉載請保留