RuleEngine规则引擎底层改造AviatorScript 之函数执行

https://gitee.com/aizuda/rule-engine-open
需求:使用上述开源框架进行改造,底层更换成AviatorScript ,函数实现改造。
原本实现方式

    @Overridepublic Object run(ExecuteFunctionRequest executeTestRequest) {Integer functionId = executeTestRequest.getId();RuleEngineFunction engineFunction = this.ruleEngineFunctionManager.getById(functionId);if (engineFunction == null) {throw new ApiException(ErrorCodeEnum.RULE9999404.getCode(),"不存在函数:{}", functionId);}String executor = engineFunction.getExecutor();if (this.applicationContext.containsBean(executor)) {Object abstractFunction = this.applicationContext.getBean(executor);// 函数测试均为固定值List<ParamValue> paramValues = executeTestRequest.getParamValues();Map<String, Value> param = new HashMap<>(paramValues.size());for (ParamValue paramValue : paramValues) {Constant constant = new Constant(paramValue.getValue(), ValueType.getByValue(paramValue.getValueType()));param.put(paramValue.getCode(), constant);}Function function = new Function(functionId, abstractFunction, ValueType.STRING, param);// 无规则参数 input==nullreturn function.getValue(null, new RuleEngineConfiguration());} else {throw new ApiException("容器中找不到{}函数", executor);}}

可以看到的是,他们首先拿到函数id,然后判断函数是否存在,然后去已经预制好的数据库表中去读取固定的函数名称,然后根据入参进行入参的判断,然后是执行。
因为我们要进行底层改造,我也就是了解了他的实现方式,并没有深入的去了解他的底层是如何实现的。
我们的话,可能入参没有那么复杂,我是自己新建了一个表来存储入参的,具体的实现也是不一样。

@Function
public class LetterToLowerCaseFunction extends AbstractFunction {@Executorpublic String executor(@Param(value = "letter",required = false) String letter) {if (letter == null) {return null;}return letter.toLowerCase();}@Overridepublic AviatorObject call(Map<String, Object> env, AviatorObject arg1) {String letter = String.valueOf(arg1.getValue(env));if (letter == null) {return null;}return new AviatorString(letter.toLowerCase());}@Overridepublic String getName() {return "letterToLowerCaseFunction";}
}

这是他们原本的函数实现,是通过Function接口,将实体类注册到spring框架中,然后将类的名字预制到数据中,通过读取某个函数将函数名读取出来,处理问题。

public class TestAviator {public static void main(String[] args) {//注册函数AviatorEvaluator.addFunction(new AddFunction());System.out.println(AviatorEvaluator.execute("add(1, 2)"));           // 3.0System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0}}class AddFunction extends AbstractFunction {@Overridepublic AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {Number left = FunctionUtils.getNumberValue(arg1, env);Number right = FunctionUtils.getNumberValue(arg2, env);return new AviatorDouble(left.doubleValue() + right.doubleValue());}public String getName() {return "add";}}

AviatorScript 这个是他官方的一个实例文档,可以看的出来实现方式大大的不一样了
其实这个项目的首先的问题是,我们需要将rule-engine 的三个项目合到一个项目中,基于这块只有繁琐但是没有难度我这边就不再提了。

@Overridepublic Object runFunction(ExecuteFunctionRequest executeTestRequest) throws Exception{Integer functionId = executeTestRequest.getId();RuleEngineFunction2 engineFunction = this.ruleEngineFunction2Manager.getById(functionId);if (engineFunction == null) {throw new ApiException(ErrorCodeEnum.RULE9999404.getCode(),"不存在函数:{}", functionId);}//获取设置对应的方法名String className = engineFunction.getClassName();String functionName = engineFunction.getFunctionName();if (this.applicationContext.containsBean(className)) {AviatorFunction abstractFunction = (AviatorFunction)this.applicationContext.getBean(className);QueryWrapper<RuleEngineFunctionParam2> queryWrapper = new QueryWrapper<>();queryWrapper.eq("function_id", engineFunction.getId());//拿到对应的参数集合,参数集合List<RuleEngineFunctionParam2> list = ruleEngineFunctionParam2Manager.list(queryWrapper);// 入参参数集合List<ParamValue> paramValues = executeTestRequest.getParamValues();HashMap hashMap = composeParams(list, paramValues);AviatorEvaluator.addFunction(abstractFunction);String params = "";for (RuleEngineFunctionParam2 ruleEngineFunctionParam2 : list) {params = params +"arg1,";}params = params.substring(0, params.length() - 1);String expression = functionName+"("+params+")";Object execute = AviatorEvaluator.execute(expression, hashMap);log.error(execute.toString());return execute;} else {throw new ApiException("容器中找不到{}函数", className+functionName);}}public HashMap composeParams(List<RuleEngineFunctionParam2> list,List<ParamValue> paramValues){HashMap params = new HashMap();for (int i = 0; i < list.size(); i++) {RuleEngineFunctionParam2 ruleEngineFunctionParam2 = list.get(i);String paramCode = ruleEngineFunctionParam2.getParamCode();for (int j = 0; j < paramValues.size(); j++) {ParamValue paramValue = paramValues.get(j);String code = paramValue.getCode();if (paramCode.equals(code)) {if(ruleEngineFunctionParam2.getValueType().equals("STRING")){params.put(paramCode, paramValue.getValue());}else if(ruleEngineFunctionParam2.getValueType().equals("COLLECTION")){String arr = paramValue.getValue();List<String> items = Arrays.asList(arr.split(","));params.put(paramCode, items);}else if(ruleEngineFunctionParam2.getValueType().equals("NUMBER")){int number = Integer.valueOf(paramValue.getValue());params.put(paramCode, number);}}}}return params;}

这一块代码是我写的函数执行的底层改造,唯一的问题就是在于传参,不过在当时看来是没有问题的

@Function
public class LetterToUpperCaseFunction extends AbstractFunction {@Executorpublic String executor(@Param(value = "letter",required = false) String letter) {if (letter == null) {return null;}return letter.toUpperCase();}@Overridepublic AviatorObject call(Map<String, Object> env,AviatorObject arg1) {String letter = env.get("letter").toString();if (letter == null) {return null;}return new AviatorString(letter.toUpperCase());}@Overridepublic String getName() {return "letterToUpperCaseFunction";}
}

在执行以上函数代码的时候一点问题都没有,那么问题出在了哪里呢,
String letter = env.get("letter").toString();这个取值,因为我这边取了一个巧,我在发现入参的类型我不好判断之后,我放弃了使用顺序确认入参这个形势,我直接使用的key-value的形势,这样可以在传参的时候一点问题都不会,如果前端传过来的入参的顺序出现问题的情况下,也会如我计划的一样执行,但是确实面临了一个问题,因为后面需要实现一个公式规则的功能
在这里插入图片描述
这个的具体研发可以放在下一篇进行讲述,然后就发现了,因为这个牵扯到给函数传值以及给代码块传值,所以陷入了一个死局,需要进行函数入参的调整,很麻烦,也不是不能做。
后面就转换了实现思路,不在使用String letter = env.get("letter").toString();方式去获取参数中的入参,尊重他人命运,前端传错了前端改吧,不过事实证明也没有出现问题,有点杞人忧天的意思了。

    @Overridepublic Object runFunction(ExecuteFunctionRequest executeTestRequest) throws Exception{Integer functionId = executeTestRequest.getId();RuleEngineFunction2 engineFunction = this.ruleEngineFunction2Manager.getById(functionId);if (engineFunction == null) {throw new ApiException(ErrorCodeEnum.RULE9999404.getCode(),"不存在函数:{}", functionId);}//获取设置对应的方法名String className = engineFunction.getClassName();String functionName = engineFunction.getFunctionName();if (this.applicationContext.containsBean(className)) {AviatorFunction abstractFunction = (AviatorFunction)this.applicationContext.getBean(className);QueryWrapper<RuleEngineFunctionParam2> queryWrapper = new QueryWrapper<>();queryWrapper.eq("function_id", engineFunction.getId());// 入参参数集合List<ParamValue> paramValues = executeTestRequest.getParamValues();AviatorEvaluator.addFunction(abstractFunction);String params = "";for (int i = 0; i < paramValues.size(); i++) {ParamValue paramValue = paramValues.get(i);String value = paramValue.getValue();if (paramValue.getValueType().equals("STRING")){params = params + "'" + value +"'" + ",";}else if (paramValue.getValueType().equals("NUMBER")){params = params + value + ",";}else if (paramValue.getValueType().equals("COLLECTION")){params = params + "'" + value +"'" + ",";}}params = params.substring(0, params.length() - 1);String expression = functionName+"("+params+")";Object execute = AviatorEvaluator.execute(expression);log.error(execute.toString());return execute;} else {throw new ApiException("容器中找不到{}函数", className+functionName);}}

修改函数显示底层,使用直接参数的方式,后端直接处理参数,方便后面代码块执行,大概是组成add(1, 2)格式。

@Function
public class LetterToUpperCaseFunction extends AbstractFunction {@Executorpublic String executor(@Param(value = "letter",required = false) String letter) {if (letter == null) {return null;}return letter.toUpperCase();}@Overridepublic AviatorObject call(Map<String, Object> env,AviatorObject arg1) {String letter = FunctionUtils.getStringValue(arg1, env);if (letter == null) {return null;}return new AviatorString(letter.toUpperCase());}@Overridepublic String getName() {return "letterToUpperCaseFunction";}
}

函数获取参数底层也进行响应的修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/301221.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解简单的shell脚本 --- 命令行解释器【Linux后端开发】

首先附上完整代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> //命令行解释器 //shell 运行原理&#xff1a;通过让子进程执行命令&#xff0c;父进…

【深入理解Java IO流0x05】Java缓冲流:为提高IO效率而生

1. 引言 我们都知道&#xff0c;内存与硬盘的交互是比较耗时的&#xff0c;因此适当得减少IO的操作次数&#xff0c;能提升整体的效率。 Java 的缓冲流是对字节流和字符流的一种封装&#xff08;装饰器模式&#xff0c;关于IO流中的一些设计模式&#xff0c;后续会再出博客来讲…

探索async/await的魔力:简化JavaScript异步编程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

Vulnhub:MHZ_CXF: C1F

目录 信息收集 arp-scan nmap nikto WEB web信息收集 dirmap gobuster ssh登录 提权 获得初始立足点 系统信息收集 横向渗透 提权 信息收集 arp-scan ┌──(root㉿ru)-[~/桌面] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:50:56:…

【Java集合】面试题汇总

Java 集合Java 集合概览1. List, Set, Queue, Map 四者的区别&#xff1f;2. ArrayList 和 Array&#xff08;数组&#xff09;的区别&#xff1f;3. ArrayList 和 Vector 的区别?4. Vector 和 Stack 的区别?&#xff08;了解即可&#xff09;5. ArrayList 可以添加 null 值吗…

paddle实现手写数字模型(一)

参考文档&#xff1a;paddle官网文档调试代码如下&#xff1a; LeNet.py import paddle import paddle.nn.functional as Fclass LeNet(paddle.nn.Layer):def __init__(self):super().__init__()self.conv1 paddle.nn.Conv2D(in_channels1,out_channels6,kernel_size5,stride…

YOLOV9 + 双目测距

YOLOV9 双目测距 1. 环境配置2. 测距流程和原理2.1 测距流程2.2 测距原理 3. 代码部分解析3.1 相机参数stereoconfig.py3.2 测距部分3.3 主代码yolov9-stereo.py 4. 实验结果4.1 测距4.2 视频展示 相关文章 1. YOLOV5 双目测距&#xff08;python&#xff09; 2. YOLOv7双目…

强化学习MPC——(一)

1.什么是强化学习 强化学习是机器学习的一种&#xff0c;是一种介于监督学习和非监督学习的机器学习方法。 学习二字就很形象的说明了这是一种利用数据&#xff08;任何形式的&#xff09;来实现一些已有问题的方法&#xff0c;学习方法&#xff0c;大致可以分为机器学习&…

说说TCP为什么需要三次握手和四次挥手?

一、三次握手 三次握手&#xff08;Three-way Handshake&#xff09;其实就是指建立一个TCP连接时&#xff0c;需要客户端和服务器总共发送3个包 主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备 过程如下&#xff…

Redis 常见面试题

目录 1. Redis是什么&#xff1f;2. Redis优缺点&#xff1f;3. Redis为什么这么快&#xff1f;4. 既然Redis那么快&#xff0c;为什么不用它做主数据库&#xff0c;只用它做缓存&#xff1f;5. Redis的线程模型&#xff1f;6. Redis 采用单线程为什么还这么快&#xff1f;7. R…

如何使用生成式人工智能撰写关于新产品发布的文章?

利用生成式人工智能撰写新产品发布文章确实是一种既有创意又高效的内容生成方式。以下是如何做到这一点的指南&#xff0c;附带一些背景信息&#xff1a; • 背景&#xff1a;在撰写文章之前&#xff0c;收集有关您的新产品的信息。这包括产品的名称、类别、特点、优势、目标受…

解决Xshell连接不上虚拟机

相信有很多同学和我一样遇到这个问题&#xff0c;在网上看了很多教程基本上都先让在虚拟机输入ifconfig命令查看ip地址&#xff0c;弄来弄去最后还是解决不了&#x1f62d;&#x1f62d;&#xff0c;其实问题根本就不在命令上&#xff0c;很大概率是我们的虚拟机没有开启网卡&a…

基于单片机便携式测振仪的研制系统设计

**单片机设计介绍&#xff0c;基于单片机便携式测振仪的研制系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机便携式测振仪的研制系统设计概要主要涉及利用单片机作为核心控制器件&#xff0c;结合测振原理和技术&#x…

python-可视化篇-turtle-画爱心

文章目录 原效果替换关键字5为8&#xff0c;看看效果改下颜色 原效果 import turtle as tt.color(red,pink) t.begin_fill() t.width(5) t.left(135) t.fd(100) t.right(180) t.circle(50,-180) t.left(90) t.circle(50,-180) t.right(180) t.fd(100) t.pu() t.goto(50,-30) t…

蓝鲸6.1 CMDB 事件推送的开源替代方案

本文来自腾讯蓝鲸智云社区用户&#xff1a;木讷大叔爱运维 背景 在蓝鲸社区“社区问答”帖子中发现这么一个需求&#xff1a; 究其原因&#xff0c;我在《不是CMDB筑高墙&#xff0c;运维需要一定的开发能力&#xff01;》一文中已经介绍&#xff0c;在此我再简单重复下&#…

JavaScript实现全选、反选功能(Vue全选、反选,js原生全选、反选)

简介&#xff1a; 在JavaScript中&#xff0c;实现全选和反选通常是通过操作DOM元素和事件监听来实现&#xff1b; 全选功能&#xff1a;当用户点击一个“全选”复选框时&#xff0c;页面中所有具有相同类名的复选框都将被选中&#xff1b; 反选功能&#xff1a;用户点击一个…

ARP寻址过程

当知道目标的IP但是不知道目标的Mac地址的时候就需要借助ARP寻址获取目标的Mac地址&#xff0c;传输层借助四元组&#xff08;源IP源端口&#xff1a;目标IP目标端口&#xff09;匹配&#xff0c;网络层借助IP匹配&#xff0c;数据链路层则根据Mac地址匹配&#xff0c;数据传输…

局域网共享文件夹怎么加密?局域网共享文件夹加密方法介绍

在企业局域网中&#xff0c;共享文件夹扮演着重要的角色。为了保护数据安全&#xff0c;我们需要加密保护局域网共享文件夹。那么&#xff0c;局域网共享文件夹怎么加密&#xff1f;下面我们来了解一下吧。 局域网共享文件夹加密方法 局域网共享文件夹加密推荐使用共享文件夹加…

在git上先新建仓库-把本地文件提交远程

一.在git新建远程项目库 1.选择新建仓库 以下以gitee为例 2.输入仓库名称&#xff0c;点击创建 这个可以选择仓库私有化还公开权限 3.获取仓库clone链接 这里选择https模式就行&#xff0c;就不需要配置对电脑进行sshkey配置了。只是需要每次提交输入账号密码 二、远…

万字源码解析!彻底搞懂 HashMap【一】:概念辨析与构造方法源码解析

HashMap 的底层原理和扩容机制一直都是面试的时候经常被问到的问题&#xff0c;同时也是集合源码中最难阅读的一部分&#x1f622;&#xff0c;之前更新的 ArrayList 源码阅读收获了很多朋友的喜欢&#xff0c;也给了我很多自信&#xff1b;本次我准备完成一个关于 HashMap 源码…