作者:知了一笑 \
起源:juejin.cn/post/7210194936276680759
一、背景
前段时间,在做我的项目重构的时候,遇到很多中央须要做很多的条件判断。当然能够用很多的 if-else 判断去解决,然而过后也不分明怎么回事,就想玩点别的。于是乎,就去调研了规定引擎。
当然,市面上有很多成熟的规定引擎,性能很多,性能很好。然而,就是想玩点不一样的(大家做技术选型别这样,这个是反面教材)。最终一款 URule 的规定引擎吸引了我,次要还是采纳浏览器可间接配置,不须要过多装置,可视化规定也做的不错。通过一系列调研,前面就把它接入了我的项目中,顺便记录下调研的后果。
二、介绍
规定引擎其实是一种组件,它能够嵌入到程序当中。将程序简单的判断规定从业务代码中剥离进去,使得程序只须要关怀本人的业务,而不须要去进行简单的逻辑判断;简略的了解是规定承受一组输出的数据,通过预约好的规定配置,再输入一组后果。
当然,市面上有很多成熟的规定引擎,如:Drools、Aviator、EasyRules 等等。然而 URule,它能够运行在 Windows、Linux、Unix 等各种类型的操作系统之上,采纳纯浏览器的编辑模式,不须要装置工具,间接在浏览器上编辑规定和测试规定。
当然这款规定引擎有开源和 pro 版本的区别,至于 pro 版是啥,懂的都懂,上面放个表格,理解下具体的区别
个性 | PRO 版 | 开源版 |
---|---|---|
向导式决策集 | 有 | 有 |
脚本式决策集 | 有 | 有 |
决策树 | 有 | 有 |
决策流 | 有 | 有 |
决策表 | 有 | 有 |
穿插决策表 | 有 | 无 |
简单评分卡 | 有 | 无 |
文件名、我的项目名重构 | 有 | 无 |
参数名、变量常量名重构 | 有 | 无 |
Excel 决策表导入 | 有 | 无 |
规定集模版保留与加载 | 有 | 无 |
中文我的项目名和文件名反对 | 有 | 无 |
服务器推送常识包到客户端性能的反对 | 有 | 无 |
常识包优化与压缩的反对 | 有 | 无 |
客户端服务器模式下大常识包的推拉反对 | 有 | 无 |
规定集中执行组的反对 | 有 | 无 |
规定流中所有节点向导式条件与动作配置的反对 | 有 | 无 |
循环规定多循环单元反对 | 有 | 无 |
循环规定中无条件执行的反对 | 有 | 无 |
导入我的项目主动重命名性能 | 有 | 无 |
规定树构建优化 | 有 | 无 |
对象查找索引反对 | 有 | 无 |
规定树中短路计算的反对 | 有 | 无 |
规定条件冗余计算缓存反对 | 有 | 无 |
基于计划的批量场景测试性能 | 有 | 无 |
常识包调用监控 | 有 | 无 |
更为欠缺的文件读写权限管制 | 有 | 无 |
常识包版本控制 | 有 | 无 |
SpringBean 及 Java 类的热部署 | 有 | 无 |
技术支持 | 有 | 无 |
三、装置应用
理论应用时,有四种应用 URule Pro 的形式,别离是嵌入式模式、本地模式、分布式计算模式以及独立服务模式。
然而咱们这里不思考 URule Pro,咱本人整个开源版,在开源版集成 springboot 的根底上做一个二次开发,搜了一圈,其实就有解决方案。
大抵的我的项目模块如下:
Spring Boot 根底就不介绍了,举荐看这个实战我的项目:
https://github.com/javastacks/spring-boot-best-practice
本人创立个空数据库,只须要在 edas-rule-server 服务中批改下数据库的配置,而后启动服务即可。第一次启动实现,数据库中会创立表。
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/urule-data?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=mysql
下面说过,它是纯用浏览器进行编辑,配置规定的,只须要关上浏览器,输出地址:http://localhost:8090/urule/frame, 看到这个界面,就阐明启动胜利了。
四、根底概念
3.1 整体介绍
先说下 URule 它的形成局部,次要是两局部:1、设计器局部 2、规定执行引擎。设计器局部次要是库文件和规定文件形成。上面看下整体的结构图
3.2 库文件
如上图介绍的,库文件有 4 种,包含变量库,参数库,常量库和动作库。其实相似于 Java 开发的零碎中的实体对象,枚举,常量以及办法。
下面说过,规定都是可视化配置的。在配置规定的过程中,就须要引入各种曾经定义好的库文件,再联合业务需要,从而配置出合乎业务场景的业务规定,所以哪里都有库文件的身影。
3.2.1 变量库文件
在业务开发中,咱们会创立很多 Getter 和 Setter 的 Java 类,比方 PO、VO、BO、DTO、POJO 等等,其实这些类 new 对象后次要起到的作用就是数据的载体,用来传输数据。
在 URule 中,变量库就是用来映射这些对象,而后能够在规定中应用,最终实现业务和规定的互动。最初上一张图,用来创立变量库
对了,下面废话了这么多可视化配置,这才是第一次展现配置界面,羞愧羞愧。
上图高深莫测,在“库”这个菜单底下右键,而后点击增加变量库即可,最初定义本人喜爱的变量库名,当然名字只反对中文或者英文,其余字符不可用。
创立完变量库后,就能够对变量库进行编辑,能够认为就是给 POJO 增加属性
也不弯弯绕绕讲什么术语,就集体了解。图右边是创立类,其中名称是它的别名,配置规定用它代替这个类。图左边是类的属性,我这里轻易写了几个,预计看了懂得都懂。
最初在业务零碎中创立对应的类,留神全限定名和配置变量库的类门路统一。
package com.cicada;
import com.bstek.urule.model.Label;
import lombok.Data;
/**
* @author 往事如风
* @version 1.0
* @date 2023/3/3 15:38
* @description
*/
@Data
public class Stu {@Label("姓名")
private String name;
@Label("年龄")
private int age;
@Label("班级")
private String classes;
}
最初说下这个 @Label
注解,这个是由 URule 提供的注解,次要是形容字段的属性,跟变量库的题目一栏统一就行。听官网介绍能够通过这个注解,实现 POJO 属性和变量库属性映射。就是 POJO 写好,而后对应规定的变量库就不须要从新写,能够间接生成。反正就有这个性能,这里就间接一笔带过了。
3.2.2 常量库文件
说到常量库,这个就能够认为是咱们 Java 零碎中的常量,枚举。比方性别,要定义枚举吧;比方对接的机构,也能够定义一个枚举吧。
当然,相似于变量库,常量库也能够实现和零碎中的枚举互相映射,这样做的益处能够防止咱们手动输出,避免输出谬误。创立常量库也比较简单,间接在“库”这个菜单下右键,“增加常量库”。
创立好常量库文件后,也会呈现如下页面:
3.2.3 参数库文件
参数库,就是 URule 规定中的长期变量,变量的类型和数量不固定。能够认为相似于 Map,实际上存储参数库的也就是个 Map。
同样的套路,间接在“库”这个菜单下右键,“增加参数库”。
能够看到,参数库曾经少了右边分类这一项,间接增加参数,抉择类型就是干,绝对简略了很多。“名称”这列我这里用了英文,就是 Map 中的 key,而“题目”这列就是在配置规定时候显示用的,中文看着比拟直观。
当然还须要留神的点是,定义的名称要保障惟一,因为 Map 中的 key 是惟一的,不然就会存在笼罩的状况。
3.2.4 动作库文件
动作库能够对配置在 spring 中的 bean 办法进行映射,而后能够在规定中间接调用这批办法。习用套路,还是在“库”菜单下右键,点击“增加动作库”。
而后我在零碎中增加了一个类 Action
,而后在类上标记@Component
注解,将该类交给 spring 的 bean 容器治理。该类中增加一些办法,在办法上标记 @ExposeAction
注解,该注解是 URule 定义的,阐明被标记的办法都会被动作库读取到。
package com.bstek.urule.cicada;
import com.bstek.urule.action.ActionId;
import com.bstek.urule.model.ExposeAction;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author 往事如风
* @version 1.0
* @date 2023/3/10 13:59
* @description
*/
@Component("action")
public class Action {@ActionId("Hello")
public String hello(){return "hello";}
@ExposeAction(value="办法 1")
public boolean evalTest(String username){if(username==null){return false;}else if(username.equals("张三")){return true;}
return false;
}
@ExposeAction(value="测试 Int")
public int testInt(int a,int b){return a+b;}
@ExposeAction(value="打印内容")
public void printContent(String username, Date birthday){SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(birthday!=null){System.out.println(username+"往年曾经"+sd.format(birthday)+"岁了!");
}else{System.out.println("Hello"+username+"");
}
}
@ExposeAction(value="打印 Stu")
public void printUser(Stu m){System.out.println("Hello"+m.getName()+", is age:"+m.getAge());
}
}
最初在动作库页面上增加 bean,“Bean Id”一列输出对应的 spring bean 的名称,这里输出 action。而后点击操作列中的小手按钮,就会弹出刚在 Action
类中标记了 ExposeAction
注解的办法。抉择一个指定的办法增加进来,最初看到办法对应的参数也会被主动加载进去。
最初,变量库、参数库、动作库、常量库这些库文件定义好后,各种规定文件配置的时候就能够导入他们。然而一旦这些库文件被某个规定文件应用,就不要随便批改库文件了。
3.3 规定集
说到规定集,顾名思义,就是配置规定了。后面定义的库文件就须要导入到规定集中去配置应用。它是应用频率最高的一个业务规定实现形式。
规定集说的是规定的汇合,由三个局部规定组成:如果、那么、否则。
在规定集的定义的形式上,URule 由向导式和脚本式两种;
- 向导式规定集:就是在页面上通过鼠标点点点,高度的可视化配置,不是开发都能懂,这也是这个规定引擎的亮点所在。
- 脚本式规定集:听名字就晓得了,这玩意要写脚本的。拉高配置门槛,须要懂点编码的人来编写。
3.3.1 向导式规定集
还是一样,首先新建。这次是在“决策集”菜单上右键,点击“增加向导式决策集”,这样就创立好一个规定集了。
在配置规定前,能够先导入后面定义好的库文件。我这里导入变量库文件,页面上点击“变量库”,而后抉择指定的变量库文件即可。如图所示;
最初,能够欢快的配置规定了,向导式没什么好讲的,都是可视化界面,点点点即可。上面是我配置的一个简略的规定集;
能够看到由三局部组成:如果、那么、否则;
- 如果:配置规定的条件;
- 那么:配置满足条件后执行的动作,个别配置变量赋值比拟多
- 否则:配置不满足条件执行的动作
最初,附上增加完规定后,通过代码去执行规定;
package com.cicada;
import cn.hutool.core.bean.BeanUtil;
import com.Result;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
import com.cicada.req.StuReq;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/**
* @author 往事如风
* @version 1.0
* @date 2023/3/10 16:47
* @description
*/
@RestController
@RequestMapping("/rule")
public class RuleDataController {@PostMapping("/stu")
public Result rule(@RequestBody StuReq stuReq) throws IOException {KnowledgeService knowledgeService = (KnowledgeService) Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
KnowledgePackage knowledgePackage = knowledgeService.getKnowledge("xxx/xxx");
KnowledgeSession knowledgeSession = KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);
Stu stu = BeanUtil.copyProperties(stuReq, Stu.class);
knowledgeSession.insert(stu);
knowledgeSession.fireRules();
return Result.success(stu.getTeacher());
}
}
申请接口,最终参数合乎配置的条件,返回“那么”中配置的输入后果。
3.3.2 脚本式规定集
脚本式的规定集,各种原理都是和向导式截然不同,无非就是拉高门槛,用写脚本的形式去实现配置的规定。这里不做过多的介绍了。
3.4 决策表
再聊下决策表,其实它就是规定集的另一种展现模式,比拟绝对规定集,我更喜爱用决策表去配置规定,应为它出现的更加直观,更便于了解。然而实质和规定集没啥区别。
也不开展过多的赘述,这里我就放一张配置过的决策表;
3.5 其余
当然,还有其余的概念和性能,这里也不一一介绍了,因为下面说的曾经是最罕用的了,想理解的能够自行去理解。其余性能包含:穿插决策表、评分卡、简单评分卡、决策树、规定流;当然,其中有些是 Pro 版的性能。
四、使用场景
最近在开发一期大版本的需要,其中就有个场景,具体如下;参加购买订单的用户都会有本人的一个职级,也能够说是角色。每个用户都会有三个职位:普通用户、会员、精英会员。
而后,每个月初都会对用户进行一次降职解决,普通用户达到要求,就会晋升为会员,会员达到要求就会晋升为精英会员。
当然,普通用户降职会员,会员降职精英会员,都会有不同的规定;
- 普通用户 -> 会员:3 个月内帮注册人数达到 3 人;3 个月内本人和底下团队的人,下单金额超过 1 万;集体的订单持续率超过 80%。
- 会员 -> 精英会员:3 个月内帮注册人数达到 6 人;3 个月内本人和底下团队的人,下单金额超过 5 万;集体的订单持续率超过 90%。
- 不能跨级降职,普通用户最多只能到会员,达到会员了能力降职到精英会员。
当然,这只是做过简化的一部分需要,我做过稍许的改变,实在的需要场景并没有这么简略。
上面,我对这个需要做一个规定的配置,这里用一个决策表进行配置;在配置规定前,我增加一个变量库文件和常量库;
最初,增加一个决策表,并进行规定配置;
能够看到,表格一共五列,其中前四列是规定,最初一列是满足规定后输入的信息。这样看着就很清晰,即便并不是技术人员,也能够轻松看懂其中的规定。
五、总结
规定引擎对于咱们的零碎而言可用可不必,它能够精益求精,帮忙咱们剥离出业务中须要进行大量判断的场景。然而,这种规定的剥离,须要咱们开发人员对需要进行了解,在了解的根底上进行抽象概念的具化。这,也是整个编程的 必经之路。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)
2. 劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!