乐趣区

关于java:Spring-Boot-URule-规则引擎可视化配置太爽了

作者:知了一笑 \
起源: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 向导式规定集

还是一样,首先新建。这次是在“决策集”菜单上右键,点击“增加向导式决策集”,这样就创立好一个规定集了。

在配置规定前,能够先导入后面定义好的库文件。我这里导入变量库文件,页面上点击“变量库”,而后抉择指定的变量库文件即可。如图所示;

最初,能够欢快的配置规定了,向导式没什么好讲的,都是可视化界面,点点点即可。上面是我配置的一个简略的规定集;

能够看到由三局部组成:如果、那么、否则;

  1. 如果:配置规定的条件;
  2. 那么:配置满足条件后执行的动作,个别配置变量赋值比拟多
  3. 否则:配置不满足条件执行的动作

最初,附上增加完规定后,通过代码去执行规定;

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 版的性能。

四、使用场景

最近在开发一期大版本的需要,其中就有个场景,具体如下;参加购买订单的用户都会有本人的一个职级,也能够说是角色。每个用户都会有三个职位:普通用户、会员、精英会员。

而后,每个月初都会对用户进行一次降职解决,普通用户达到要求,就会晋升为会员,会员达到要求就会晋升为精英会员。

当然,普通用户降职会员,会员降职精英会员,都会有不同的规定;

  1. 普通用户 -> 会员:3 个月内帮注册人数达到 3 人;3 个月内本人和底下团队的人,下单金额超过 1 万;集体的订单持续率超过 80%。
  2. 会员 -> 精英会员:3 个月内帮注册人数达到 6 人;3 个月内本人和底下团队的人,下单金额超过 5 万;集体的订单持续率超过 90%。
  3. 不能跨级降职,普通用户最多只能到会员,达到会员了能力降职到精英会员。

当然,这只是做过简化的一部分需要,我做过稍许的改变,实在的需要场景并没有这么简略。

上面,我对这个需要做一个规定的配置,这里用一个决策表进行配置;在配置规定前,我增加一个变量库文件和常量库;

最初,增加一个决策表,并进行规定配置;

能够看到,表格一共五列,其中前四列是规定,最初一列是满足规定后输入的信息。这样看着就很清晰,即便并不是技术人员,也能够轻松看懂其中的规定。

五、总结

规定引擎对于咱们的零碎而言可用可不必,它能够精益求精,帮忙咱们剥离出业务中须要进行大量判断的场景。然而,这种规定的剥离,须要咱们开发人员对需要进行了解,在了解的根底上进行抽象概念的具化。这,也是整个编程的 必经之路

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

退出移动版