学习更多Java干货,戳上面的 蓝色字题关注我!
最近项目需要增加风控系统,在经过一番调研以后决定使用Drools规则引擎。因为项目是基于SpringCloud的架构,所以此次学习使用了SpringBoot2.0版本结合Drools7.14.0.Final版本。
引入依赖
<dependency> <groupId>org.droolsgroupId> <artifactId>drools-coreartifactId> <version>7.14.0.Finalversion>dependency><dependency> <groupId>org.kiegroupId> <artifactId>kie-springartifactId> <version>7.14.0.Finalversion>dependency>
创建配置类
@Configurationpublic 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文件
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
@RunWith(SpringRunner.class)@SpringBootTestpublic class DroolsApplicationHelloWordTests { @Autowired KieSession kieSession;}
上方注入的kieSession对象就是以后与Drools打交道最常用的一个对象了,通过它可以直接操作在配置类kieFileSystem方法中加载的所有的规则文件
2. 编写测试代码
@Testpublic void testHelloWord() { kieSession.fireAllRules();}
kieSession.fireAllRules方法是执行所有的规则,在运行了这个测试方法之后我们应该就可以看到控制台打印的一句hello,word!了
基础学习
1. 向规则文件传参
1. 在entity包下新增一个User的实体类
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
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. 编写测试代码
@Testpublic 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()本来就是执行所有被加载的规则的。那么避免这种情况的办法就是执定本次执行的规则
@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用于判断对象的某个字段是否包含另外一个对象
rule "contains" when $s:String() $u:User(name contains $s) then System.err.println("用户张三存在");end
@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用于判断对象的某个字段是否存在一个集合中
rule "memberOf" when $list:List() $u:User(name memberOf $list) then System.err.println("用户李四存在");end
@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就是用于匹配正则表达式的了
rule "matches" when $u:User(name matches "张.*") then System.err.println("用户张xx存在");end
@Test public void testMatches() { User user=new User("张三",18); kieSession.insert(user); kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("matches")); }
not matches不用我说了吧
点击下方“阅读原文”查看源码!!!