架构(十四)动态Groovy脚本

news2025/1/11 8:52:21

一、引言

        最近作者的平台项目需要实现前端输入脚本,后端在用户设置好的一些情况下运行这段脚本。后端是java,所以我们采用Groovy脚本。

        所以要实现的功能就是动态的Groovy脚本!

二、Groovy介绍

        了解groovy和python的就可以直接到第三章了

2.1 优缺点

        Groovy是一种基于Java虚拟机(JVM)的动态编程语言,它结合了静态类型语言和动态类型语言的特性。下面是Groovy的一些优点和缺点:


优点:
1、与Java的无缝集成

        Groovy可以直接在Java代码中使用,与Java代码可以互相调用。这种无缝集成使得在现有Java项目中引入Groovy变得非常容易。


2、动态语言特性

        Groovy支持动态类型、元编程和闭包等特性,使得代码编写更加灵活和简洁。它提供了更简洁的语法和更少的样板代码,提高了开发效率。


3、丰富的语法特性

        Groovy提供了许多便利的语法特性,如集合操作、字符串处理、正则表达式等。它还支持DSL(领域特定语言)的编写,使得代码更易读、更具表达力。


4、可读性强

        Groovy的语法设计力求简洁、易读,使得代码更加清晰和易于理解。它具有更少的冗余代码和更自然的表达方式。


缺点:
1、性能

        相对于Java,Groovy的执行速度可能会慢一些。这是由于动态类型和动态特性的引入,以及Groovy的动态编译过程所导致的。然而,对于大多数应用程序而言,Groovy的性能仍然足够好。


2、工具和库支持

        相对于Java,Groovy的工具和库生态系统相对较小。虽然Groovy可以直接使用Java的库,但有些库可能没有专门为Groovy进行优化或提供更高级的Groovy API。

2.2  Groovy和Python比较

        Groovy和Python都是流行的动态编程语言,它们有一些相似之处,但也有一些区别。下面是Groovy和Python之间的一些比较


1、语法和风格


        Groovy的语法与Java非常相似,因此对于Java开发者来说,学习和使用Groovy相对容易。它也支持类似Java的面向对象编程风格。
        Python的语法更加简洁和优雅,具有非常清晰的语法结构。它采用了缩进来表示代码块,使得代码更易读。


2、类型系统


        Groovy是一种动态类型语言,它允许变量在运行时动态地改变其类型。这使得Groovy更加灵活,但也可能导致一些类型相关的错误。
        Python也是一种动态类型语言,但它具有更严格的类型系统。Python 3引入了类型提示(Type Hints)的功能,可以在代码中指定变量的类型,提高了代码的可读性和可维护性。


3、生态系统和库支持


        Python拥有非常庞大和活跃的生态系统,有大量的第三方库和工具可供使用。这使得Python成为数据科学、Web开发、自动化脚本等领域的首选语言。
        Groovy的生态系统相对较小,但它可以直接使用Java的库。对于Java开发者来说,Groovy可以无缝集成到Java项目中,享受Java生态系统的优势。


4、性能


        通常情况下,由于其动态特性,Groovy的执行速度可能比Python慢一些。然而,对于大多数应用程序而言,这种差异并不明显,因为它们通常受限于I/O等其他因素。
        Python通常被认为是一种相对较慢的语言,但通过使用C扩展和优化技术,可以提高Python的性能。


5、用途和应用领域


        Python在数据科学、人工智能、Web开发、自动化脚本等领域具有广泛的应用。它拥有丰富的库和工具,如NumPy、Pandas、Django等,使得开发更加便捷。
        Groovy在Java开发领域应用广泛,特别适用于构建DSL(领域特定语言)、编写脚本和快速原型开发。

三、动态脚本

1、pom

<dependency>
                <groupId>org.codehaus.groovy</groupId>
                <artifactId>groovy</artifactId>
                <version>3.0.8</version>
            </dependency>

2、前端

        既然是动态的,就需要用户随意填写,即使有限制也是在后端进行权限之类的限制,然后向用户说明使用规则给用户提供一些基础类

        所以前端只要给一个输入脚本的内容框就好

3、后端

3.1 运行groovy脚本

        用户的脚本其实就是一个字符串,要运行这段字符串,怎么实现?

        作者使用的是GroovyShell,看起来很简单,但是这只是开始,随着后面的拓展需要考虑很多细节

String script = param.getContent();
        // 创建GroovyShell
        GroovyShell shell = new GroovyShell();
            // 运行Groovy脚本
            Object response = shell.evaluate(script);

3.2 传入参数给脚本       

        第一个要考虑的就是一些参数可能是前面的逻辑带过来的,比如操作类型、订单、费用之类的,那就需要把这些参数带给脚本

        // 创建绑定变量
        Binding binding = new Binding();
        binding.setVariable("orderId", orderId);
        binding.setVariable("parameter", parameter);
        // 创建GroovyShell
        GroovyShell shell = new GroovyShell(binding);
            // 运行Groovy脚本
            Object response = shell.evaluate(script);

3.2 基础工具

        脚本是要用来做逻辑的,不可能每次都是前面的逻辑把数据准备好,然后传递给脚本计算。

        所以要把一些基础功能包装好,比如查数据库、调用接口

        查数据库就用到之前的一篇文章了,通过动态的多数据源暴露出一个工具类,让脚本拿到这个类,指定数据库和sql就可以进行查询:

架构(十)Mybatis动态数据源及其原理_mybatis 动态数据源-CSDN博客

@Component
public class CommonGateWayImpl implements CommonGateWay {

    @Resource
    private CommonQueryMapper commonQueryMapper;

    @Override
    public List<HashMap> executeSql(String db, String sql) {
        DataSourceThreadLocal.setDB(db);
        List<HashMap> res = commonQueryMapper.query(sql);
        DataSourceThreadLocal.clearDB();
        return res;
    }
}

        调用接口基本上每家公司都不太一样,但是原理是相似的,都是根据系统标识找到一个可用的节点,拿到节点的url,调用这个url+方法,把请求参数转json作为消息体传过去

        所以调用接口的工具类至少要有这两个方法,合并成一个也行,需要跟自己公司的注册中心打通一下,让中间件团队提供查询节点的功能。然后如果有权限校验的也要开一下,免得调用不通。

@Component
public class ExecuteInterfaceUtil {


    public String getUrl(String systemCode, String action) {
        List<UrlEntity> urlList = //查系统所有节点
        if (CollectionUtilsExt.isBlank(urlList)) {
            return null;
        }
        String url = urlList.size() > 0 ? urlList.get(0).getUrl() : "";
        url = url + action;
        return url;
    }

    public String execute(String url, String body, HashMap parameter)  {
        // 发送Http请求

        return response;
    }
}

3.3 SpringBoot取Bean

        也是在写脚本执行的时候发现还需要一个功能,就是从SpringBoot的bean工厂取Bean,不然自己new一个对象是调用不了这里面的方法的,而且new也不优雅,这是基础工具又不是数据包装

        所以就要找一下实现的方法,其实经常用的功能

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringUtil implements ApplicationContextAware {
    // 应用上下文环境
    private static ApplicationContext context;

    @SuppressWarnings("static-access")
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }

    /**
     * 根据bean name 获取对象
     *
     * @param name
     * @return
     */
    public static <T> T getBean(String name) {
        return (T)context.getBean(name);
    }

    /**
     * 根据Class获取对象
     * 
     * @param clazz
     * @param <T>
     * @return
     * @throws BeansException
     */
    public static <T> T getBean(Class<T> clazz) throws BeansException {
        return context.getBean(clazz);
    }
}

4、脚本

        底层都准备好了,那就可以写脚本试一试了,包名和导入的工具类路径都是必须的,groovy执行的时候都需要导入

        这里演示了一下查完db数据之后,再去查接口明细

package ***.groovy

import ***.JSONUtil
import ***.StringUtilsExt
import ***.SpringUtil
import ***.ExecuteInterfaceUtil
import ***.CommonGateWay
import org.apache.curator.shaded.com.google.common.collect.Lists

CommonGateWay commonGateWay = SpringUtil.getBean(CommonGateWay.class)
ExecuteSoaUtil executeInterfaceUtil = SpringUtil.getBean(ExecuteSoaUtil.class)
List<HashMap> dataList = commonGateWay.executeSql("orderDb", "SELECT TOP 10 orderId FROM order ORDER BY update_time DESC;")
if (dataList == null || dataList.size() == 0) {
    return dataList
}
String url = executeInterfaceUtil.getUrl("abc", "getDetail")
if (url == null || url.length() == 0) {
    return dataList
}
List<HashMap> res = new ArrayList<>()
for (HashMap map : dataList) {
    HashMap body = new HashMap(2)
    body.put("orderIdList", Lists.newArrayList(map.get("orderid")))
    String response = executeInterfaceUtil.execute(url, JSONUtil.toJson(body), null)
    if (StringUtilsExt.isBlank(response)) {
        return res
    }
    HashMap<String, Object> interfaceMap = JSONUtil.parse(response, HashMap.class)
    res.add(soaMap)
    for (Map.Entry<String, Object> entry : interfaceMap.entrySet()) {
        Object value = entry.getValue()
        if (!(value instanceof Integer) && !(value instanceof String)) {
            entry.setValue(JSONUtil.toJsonNoException(value))
        }
    }
    if (!interfaceMap.containsKey("responseCode") || (int) interfaceMap.get("responseCode") != SUCCESS) {
        return res
    }
}
return res

        再看看执行结果

 四、总结

        动态的脚本这样就实现了,其实用python也可以,java也有支持python的执行库,不过用起来就有点曲折了。

        即使用得是和java无缝集成的Groovy,过程也是有点绕弯的,而且还有一些小坎没写,写出来会降低作者的逼格,毕竟很多时候调试开发遇到的都是一些很小的问题一下子没转过来,事后想想又觉得脑子丢了一会。

        有兴趣的同学可以在评论区和作者讨论自己项目的疑难杂症!一起交流进步

                

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【git】提交信息写错了,使用 amend 或者 reset 修改最近一次的提交信息 ,修改上上次/以前的提交信息

如果你的提交信息写错了&#xff0c;比如下面&#xff0c;你想修改【初始化项目】这5个字 修改最近一次的提交新的两个办法 &#xff08;1&#xff09;使用 reset 把这个提交重置&#xff0c;然后重新提交&#xff0c;reset 的使用方法请参考这篇文章。但是 reset 这种方法只能…

django请求生命周期流程图,路由匹配,路由有名无名反向解析,路由分发,名称空间

django请求生命周期流程图 浏览器发起请求。 先经过网关接口&#xff0c;Django自带的是wsgiref&#xff0c;请求来的时候解析封装&#xff0c;响应走的时候打包处理&#xff0c;这个wsgiref模块本身能够支持的并发量很少&#xff0c;最多1000左右&#xff0c;上线之后会换成u…

Redis(十四)双写一致性工程案例

文章目录 问题概述canal功能安装部署mysql配置canal服务端canal客户端&#xff08;Java程序&#xff09; 问题概述 canal https://github.com/alibaba/canal 功能 数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、倒排索引等)业务 cache 刷新带业务逻辑的增量数据…

springboot当中使用EMQX(MQTT协议)

本篇博客主要围绕EMQX是什么&#xff1f;、能干什么&#xff1f;、怎么用&#xff1f; 三点来进行整理。 1、MQTT协议 1.1、MQTT简介 在了解EMQX前首先了解一下MQTT协议&#xff0c;MQTT 全称为 Message Queuing Telemetry Transport&#xff08;消息队列遥测传输&#xff0…

IDEA查询对应功能的快捷键

首先要知道快捷键的key叫什么&#xff0c;然后通过key来找到对应的快捷键 比如下面这个查找删除导入未使用的类 跳转 或者安装对应插件

多维时序 | Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、BiLSTM的多变量时间序列预测

多维时序 | Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、BiLSTM的多变量时间序列预测 目录 多维时序 | Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、BiLSTM的多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现基于VMD-DBO-BiLSTM、VMD-BiLSTM、…

【Docker】docker常用命令

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、镜像仓库架构图 二、docker仓库命令 1、登录仓库docker login 2、拉取镜像docker pull 3、推送镜像docker pus…

TOUCHDESIGNER(1-2) 元件

元件来了 一级 二级 三级!!!

Git 客户端可视化工具tortoisegit

Git 使用教程 git一点通 (kdocs.cn) 二、Git 客户端可视化工具-推荐 1.常用工具 tortoisegit 官网 https://tortoisegit.org/ 推荐 sourcetree 官网 https://www.sourcetreeapp.com/ 2.tortoisegit安装 2.1 下载安装包 2.2 下载语言包 2.3 安装 2.4 安装语言包 5.使用 5.1 新建…

说一下JVM类加载机制?

Java中的所有类&#xff0c;都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类&#xff0c;而它的工作就是把class文件从硬盘读取到内存中。 在写程序的时候&#xff0c;我们几乎不需要关心类的加载&#xff0c;因为这些都是隐式装载的&#xff0c;除非我们有特殊…

Python学习-数字类型、布尔类型

八、数字类型 1、整数&#xff08;integers&#xff09; Python的除法就算能被整除&#xff0c;也会产生带小数的结果。小数在Python中是以浮点数的形式存在的。 比如&#xff1a; >>>6/2 3.02、浮点数&#xff08;floating point numbers&#xff09; Python的浮…

排序算法之——直接插入排序

直接插入排序——以升序排列为例 1.1基本思想1.2动态图示感知1.3静态图示详解1.4代码实现1.5时间复杂度1.5.1最好情况1.5.2最差情况 1.6空间复杂度1.7稳定性1.7.1一个小问题 1.1基本思想 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直…

鸿蒙小案例-五子棋

鸿蒙小案例-五子棋 1.准备组件(组件布局) 2.下棋功能实现 3.机器人下棋功能实现 4.赢棋功能实现 5.附属功能实现刚开始以为挺简单的&#xff0c;越写越…emo 因为代码有点多&#xff0c;所以这里就简单讲下逻辑&#xff0c;文末贴上代码 逻辑只是我个人想的&#xff0c;不代…

Learn HTML in 1 hour

website address https://www.youtube.com/watch?vHD13eq_Pmp8 excerpt All right, what’s going on? everybody. It’s your Bro, hope you’re doing well, and in this video I’m going to help you started with html; so sit back, relax and enjoy the show. If y…

ABAP 导入Excel表示例程序

目录 ABAP 导入excel示例程序创建程序使用的结构上传下载模板 ABAP 导入excel示例程序 批量导入程序&#xff0c;需要使用到导入模板&#xff0c;首先需要创建程序&#xff0c;之后是需要创建excel导入模板&#xff0c;并且需要将excel导入模板上传到SAP系统里面&#xff0c;之…

【linux】查看openssl程序的安装情况

【linux】查看openssl程序的安装情况 1、查看安装包信息 $ rpm -qa |grep openssl 2、安装路径 $ rpm -ql openssl $ rpm -ql openssl-libs $ rpm -ql openssl-devel 3、相关文件和目录 /usr/bin/openssl /usr/include/openssl /usr/lib64/libssl.so.* /usr/lib64/libcrypto…

idea自动导包。已经无用导包自动移除

1.setting中勾选这两项&#xff0c;然后点击apply即可

【DDD】学习笔记-薪资管理系统的场景驱动设计

场景驱动设计的起点是领域场景&#xff0c;它不一定需要与事件风暴结合&#xff0c;只要识别并确定了领域场景&#xff0c;就可以进行任务分解。每个分解出来的子任务都可以视为是职责。分配职责时&#xff0c;场景驱动设计规定了履行职责的角色构造型&#xff0c;其中&#xf…

MongoDB 权限管理

文章目录 前言1. 权限控制1.1 MongoDB 默认角色1.1.1 读写角色1.1.2 管理角色1.1.3 其他角色1.1.4 超级用户角色 1.2 用户管理1.2.1 查看用户1.2.2 创建新用户1.2.3 调整角色1.2.4 删除用户1.2.4 修改密码 前言 上一篇 《MongoDB 单机安装部署》 文章中&#xff0c;为 MongoDB…

[SwiftUI]启动页LaunchScreen.storyboard中适配状态栏加安全区域的高度

如下图&#xff0c;我有一个需求。在启动页&#xff08;LaunchScreen.storyboard&#xff09;和引导页&#xff08;GuideView&#xff09;的黑色背景上&#xff0c;使用了同一张正方形图片。要求从启动页切换到引导页时&#xff0c;这两张相同的图片的过渡要无缝衔接&#xff0…