没有规矩,不成方圆;
一、布景
前段时间,在做项目重构的时分,遇到许多当地需求做许多的条件判别。当然能够用许多的if-else判别去处理,可是其时也不清楚怎么回事,就想玩点其他。于是乎,就去调研了规矩引擎。
当然,市道上有许多老练的规矩引擎,功用许多,功用很好。可是,便是想玩点不相同的(咱们做技术选型别这样,这个是反面教材)。终究一款URule的规矩引擎招引了我,首要仍是选用浏览器可直接装备,不需求过多安装,可视化规矩也做的不错。经过一系列调研,后边就把它接入了项目中,顺便记录下调研的成果。
二、介绍
规矩引擎其实是一种组件,它能够嵌入到程序当中。将程序杂乱的判别规矩从事务代码中剥离出来,使得程序只需求关心自己的事务,而不需求去进行杂乱的逻辑判别;简略的了解是规矩接受一组输入的数据,经过预定好的规矩装备,再输出一组成果。
当然,市道上有许多老练的规矩引擎,如:Drools、Aviator、EasyRules等等。可是URule,它能够运行在Windows、Linux、Unix等各种类型的操作体系之上,选用纯浏览器的修正方式,不需求安装工具,直接在浏览器上修正规矩和测验规矩。
当然这款规矩引擎有开源和pro版别的差异,至于pro版是啥,懂的都懂,下面放个表格,了解下详细的差异
特性 | PRO版 | 开源版 |
---|---|---|
导游式决议计划集 | 有 | 有 |
脚本式决议计划集 | 有 | 有 |
决议计划树 | 有 | 有 |
决议计划流 | 有 | 有 |
决议计划表 | 有 | 有 |
交叉决议计划表 | 有 | 无 |
杂乱评分卡 | 有 | 无 |
文件名、项目名重构 | 有 | 无 |
参数名、变量常量名重构 | 有 | 无 |
Excel决议计划表导入 | 有 | 无 |
规矩集模版保存与加载 | 有 | 无 |
中文项目名和文件名支撑 | 有 | 无 |
服务器推送常识包到客户端功用的支撑 | 有 | 无 |
常识包优化与压缩的支撑 | 有 | 无 |
客户端服务器方式下大常识包的推拉支撑 | 有 | 无 |
规矩集中履行组的支撑 | 有 | 无 |
规矩流中所有节点导游式条件与动作装备的支撑 | 有 | 无 |
循环规矩多循环单元支撑 | 有 | 无 |
循环规矩中无条件履行的支撑 | 有 | 无 |
导入项目主动重命名功用 | 有 | 无 |
规矩树构建优化 | 有 | 无 |
目标查找索引支撑 | 有 | 无 |
规矩树中短路核算的支撑 | 有 | 无 |
规矩条件冗余核算缓存支撑 | 有 | 无 |
基于计划的批量场景测验功用 | 有 | 无 |
常识包调用监控 | 有 | 无 |
更为完善的文件读写权限操控 | 有 | 无 |
常识包版别操控 | 有 | 无 |
SpringBean及Java类的热布置 | 有 | 无 |
技术支撑 | 有 | 无 |
三、安装运用
实际运用时,有四种运用URule Pro的办法,分别是嵌入式方式、本地方式、分布式核算方式以及独立服务方式。
可是咱们这儿不考虑URule Pro,咱自己整个开源版,在开源版集成springboot的基础上做一个二次开发,搜了一圈,其实就有处理计划。大致的项目模块如下:
自己创立个空数据库,只需求在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%。
- 不能跨级提升,普通用户最多只能到会员,到达会员了才干提升到精英会员。
当然,这仅仅做过简化的一部分需求,我做过稍许的改动,实在的需求场景并没有这么简略。
下面,我对这个需求做一个规矩的装备,这儿用一个决议计划表进行装备;在装备规矩前,我增加一个变量库文件和常量库;
终究,增加一个决议计划表,并进行规矩装备;
能够看到,表格总共五列,其间前四列是规矩,终究一列是满意规矩后输出的信息。这样看着就很明晰,即便并不是技术人员,也能够轻松看懂其间的规矩。
五、总结
规矩引擎关于咱们的体系而言可用可不用,它能够如虎添翼,协助咱们剥离出事务中需求进行很多判别的场景。可是,这种规矩的剥离,需求咱们开发人员对需求进行了解,在了解的基础上进行抽象概念的具化。这,也是整个编程的必经之路。
六、参阅源码
编程文档:
https://gitee.com/cicadasmile/butte-java-note
应用仓库:
https://gitee.com/cicadasmile/butte-flyer-parent