Springboot项目中加载Groovy脚本并调用其内部方代码实现
前言
项目中部署到多个煤矿的上,每一种煤矿的情况都相同,涉及到支架的算法得写好几套,于是想到用脚本实现差异变化多的算法!一开始想到用java调用js脚本去实现,因为这个不需要引入格外的包,js对我来说也没啥学习成本,后来发现js的方法的参数中没办法使用java的对象传参。如果要把java对象分解成多个基本类型的参数传递的话,js的代码实现就变复杂和臃肿了。于是改用groovy脚本去实现,后来经过两个小时的实现,终于调通!groovy的语法和java类似,学习成本很低,且groovy可以引入java的类使用java的对象,调用java的方法也很简单,下面给出实现的代码图文教程。
Groovy简介
Groovy是一种基于JVM(Java虚拟机)的动态编程语言,它具有Java的兼容性和许多强大的功能,可以用来快速开发Java应用程序。
以下是Groovy的一些主要特点:
- 静态类型:Groovy是静态类型的语言,这意味着你可以在编译时检测到许多常见的错误,从而提高代码的质量和可维护性。
- 动态类型:同时,Groovy也是动态类型的语言,这意味着你可以在运行时动态地改变变量的类型,这使得Groovy代码更加灵活和易读。
- 强大的语法:Groovy的语法比Java更简洁、更易读。它支持多种编程范式,如面向对象编程和函数式编程。
- 与Java无缝集成:Groovy可以与Java代码无缝集成,这意味着你可以在Groovy代码中使用Java类库,反之亦然。这使得Groovy成为Java开发者的强大工具。
- 简洁的语法:与Java相比,Groovy的语法更为简洁。它支持许多Java不支持的特性,如可选的括号、可选的类型声明等。
- 强大的元编程能力:Groovy具有强大的元编程能力,它允许你在运行时动态地修改代码。这使得Groovy成为快速开发原型或快速实现想法的有力工具。
- 测试驱动开发:Groovy支持测试驱动开发(TDD),它使得你可以快速编写测试代码并以此驱动你的业务逻辑代码。
- 闭包:Groovy支持闭包,这是一种可以包含代码块的语法结构,可以作为参数传递给函数,也可以赋值给变量。
- 运行时类型检查:虽然Groovy是动态类型的语言,但它也支持运行时类型检查,这使得你可以在运行时捕获许多类型错误。
- AST变换:Groovy允许你直接操作Java字节码,这使得你可以在编译时对代码进行修改。这是许多静态类型语言无法提供的功能。
总的来说,Groovy是一种强大、灵活且易于使用的编程语言,无论你是一个Java开发者还是一个想要使用JVM的语言的人,你都可以从Groovy中受益。
教程
引入依赖
首先在springboot项目中引入groovy的依赖,maven引入配置如下:
<dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy-all</artifactId><version>2.4.11</version></dependency>
java加载使用脚本工具类
新建一个ScriptProvider脚本使用类,代码如下:
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.io.File;
import java.io.IOException;
import java.util.List;/*** @author Lenovo*/
@Component
@Slf4j
public class ScriptProvider {private GroovyObject groovyObject;@Value("${app.work-face}")private void loadScript(String name){try (GroovyClassLoader classLoader = new GroovyClassLoader()) {Class<?> groovyClass = classLoader.parseClass(new File("etc/"+name+".groovy"));groovyObject= (GroovyObject) groovyClass.newInstance();} catch (InstantiationException | IOException | IllegalAccessException e) {log.error(e.getMessage());}}public void overloadScript(String filePath){try (GroovyClassLoader classLoader = new GroovyClassLoader()) {Class<?> groovyClass = classLoader.parseClass(new File(filePath));groovyObject= (GroovyObject) groovyClass.newInstance();} catch (InstantiationException | IOException | IllegalAccessException e) {log.error(e.getMessage());}}public double calStentHeight(int i, List<DataValue> dataValues){Object[] objects = new Object[]{i,dataValues};Object result=groovyObject.invokeMethod("calStentHeight",objects);return Double.parseDouble(result.toString());}public double revisedStentHeight(int i, List<DataValue> dataValues){Object[] objects = new Object[]{i,dataValues};Object result=groovyObject.invokeMethod("revisedStentHeight",objects);return Double.parseDouble(result.toString());}public Object revisedStentStroke(int i, List<DataValue> dataValues) {Object[] objects = new Object[]{i,dataValues};Object result=groovyObject.invokeMethod("revisedStentStroke",objects);return Double.parseDouble(result.toString());}
}
代码解析
- 在spring配置文件中配置不同的脚本名,通过@Value(“${app.work-face}”) 注解 ,在项目启动时加载对应的groovy脚本文件。
- invokeMethod(“calStentHeight”,objects); calStentHeight 是groovy脚本中定义的方法,objects数组是groovy脚本中方法的参数,objects数组数组的元素顺序和方法的参数保持一致。
- new File(“etc/”+name+“.groovy”) 是读入项目根目录下,etc文件夹下的某个groovy脚本文件,打成jar运行的时候,读取的是jar同级目录下,etc文件夹下的某个groovy脚本文件。

groovy脚本
创建一个groovy脚本,以4703.groovy为例,代码如下:
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValuedef calStentHeight(int i,List<DataValue> dataValues){return dataValues.get(i).getValue().getValue();
}def revisedStentHeight(int i,List<DataValue> dataValues){double d=dataValues.get(i).getValue().getValue();int stentNo=i+1;if(stentNo<=2||stentNo>=92){if(d>3.6){d=3.6;}if(d<=0){d=3.3;}}else{if(d>1.9){d=stentNo==3?1.9:dataValues.get(i-1).getValue().getValue();}if(d<=0){d=1.9;}}return d;
}def revisedStentStroke(int i,List<DataValue> dataValues){double d=dataValues.get(i).getValue().getValue();int stentNo=i+1;if(stentNo<=2||stentNo>=92){if(d>900D){d=900D;}if(d<=0D){d=0D;}}else{if(d>900D||d<=0D){d=dataValues.get(i-1).getValue().getValue();;}}return d;
}
代码解析:
- DataValue对象是java opc开源工具获取,opc点位对应值返回的对象。
- groovy的方法内部可以向java一样编写
java调用groovy脚本的方法
在springboot中调用groovy脚本中的方法,示例代码如下:
@Resourceprivate ScriptProvider scriptProvider;StentsDTO.putStentHeight(key.getPointAddress(),scriptProvider.calStentHeight(i,dataValues));objValue= scriptProvider.revisedStentHeight(i,dataValues);
代码解析
- 通过@Resource注解将ScriptProvider类注入到spring的容器中,通过ScriptProvider的实例对象,调用内部方法。
groovy脚本修改监听类实现
java无法是实现对具体某个文件操作事件的监听,只能实现文件夹和文件夹下的文件的事件的监听。我们可以通过监听文件夹监听实现对文件的监听。代码如下(代码中只有文件修改事件的监听):
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.nio.file.*;/*** @author tarzan*/
@Component
@AllArgsConstructor
@Slf4j
public class FileSystemWatcher {private final ScriptProvider scriptProvider;public void modifyWatch(){try {watch(StandardWatchEventKinds.ENTRY_MODIFY);} catch (IOException | InterruptedException e) {log.error(e.getMessage());}}private void watch(WatchEvent.Kind<Path> eventKind) throws IOException, InterruptedException {// 定义你想要监听的路径Path path = Paths.get("etc");// 创建 WatchServiceWatchService watchService = FileSystems.getDefault().newWatchService();// 将路径注册到 WatchService,并指定你想要监听的事件类型path.register(watchService, eventKind);while (true) {// 获取下一个文件系统事件WatchKey key = watchService.take();for (WatchEvent<?> event : key.pollEvents()) {// 获取事件类型WatchEvent.Kind<?> kind = event.kind();if (kind == StandardWatchEventKinds.OVERFLOW) {continue;}// 获取发生事件的文件WatchEvent<Path> ev = (WatchEvent<Path>) event;Path fileName = ev.context();// 打印出发生事件的文件名和事件类型boolean eventFlag=!fileName.toFile().getName().endsWith("~");if(eventFlag){log.info("etc/"+fileName +" be modified");scriptProvider.overloadScript("etc/"+fileName);}}//睡眠1秒Thread.sleep(1000);// 重置 WatchKey,以便接收下一个事件boolean valid = key.reset();if (!valid) {break;}}}}
代码解析
- 代码通过监听etc文件,当监听到etc文件夹下的文件进行修改操作或者覆盖操作的事件,就会重新加载修改后的grooy脚本文件。
结语
如果你对文章有疑问或者更好的见解,请在评论区留言!如果文章对你有帮助,请点赞收藏!!!
相关文章:
Springboot项目中加载Groovy脚本并调用其内部方代码实现
前言 项目中部署到多个煤矿的上,每一种煤矿的情况都相同,涉及到支架的算法得写好几套,于是想到用脚本实现差异变化多的算法!一开始想到用java调用js脚本去实现,因为这个不需要引入格外的包,js对我来说也没…...
为什么要做数据可视化
在当今信息爆炸的时代,数据已成为个人和企业最宝贵的资产之一。然而,仅仅拥有大量的数据并不足以支持明智的决策。数据可视化,作为一种将数据转化为图形形式的技术和方法,可以帮助我们更好地理解和分析数据,从而更准确…...
0基础学习VR全景平台篇 第108篇:全景图细节处理(下,航拍)
上课!全体起立~ 大家好,欢迎观看蛙色官方系列全景摄影课程! (调色前图库) (原图-大图) 一、导入文件 单击右下角导入按钮,选择航拍图片所在文件夹,选择图片࿰…...
linux查看文件内容命令more/less/cat/head/tail/grep
1.浏览全部内容more/less 文件: more:可以查看文件第一屏的内容,同时左下角有一个显示内容占全部文件内容的百分比,空格键会显示下一屏的内容,直到文件末尾 [rootmaster data]# more file1less:相较于mor…...
VBA窗体跟随活动单元格【简易版】
本篇博客与以往的风格不同,先上图再讲解。 这个效果是不是很酷,VBA窗体(即UserForm,下文中简称为窗体)可以实现很多功能,例如:用户输入数据,提供选项等等。如本博客标题标注&#…...
epiiAdmin框架注意事项
1,epiiAdmin文档地址: 简介/安装 EpiiAdmin中文文档 看云 2,项目性想新建模块 composer.json文件——autoload选项——psr-4下增加模块名称,然后执行composer update命令。 "autoload": {"psr-4": {"…...
数据仓库与ETL
什么是数据仓库 一种用于存储和管理数据的系统,提供一种统一方式,将不同来源、不同方式、不同时间的数据集成在一起。 数据仓库结构 主题域:一个特定领域的数据集,比如营销、销售、客户、库存等。 维度:定义数据的不…...
Centos7安装Gitlab--gitlab--ee版
1 安装必要依赖 2 配置GitLab软件源镜像 3 下载安装GitLab 4 查看管理员root用户默认密码 5 登录GitLab 6 修改密码 7 gitlab相关命令 1 安装必要依赖 sudo yum install -y curl policycoreutils-python openssh-server perl sudo systemctl enable sshd sudo systemctl sta…...
主题教育问题清单及整改措施2023年-主题教育对照六个方面个人剖析材料
无论前方路途多么坎坷,都要保持内心的坚定和勇敢。生活中没有什么不可战胜的困难,只有我们是否愿意去面对和克服。要相信自己的能力,相信自己拥有足够的智慧和力量去应对一切挑战 每一次的努力都不会白费,每一次的奋斗都是在为自己…...
php新手实战:自定义书源下载api
网上有很多第三方小说网站提供小说下载,而下载的过程无非就是搜索书籍,然后找到下载链接点击下载即可。只是类似这种“良心”的小说网站实在是太少。大多数仅支持在线阅读。而如今,我却要利用这种为数不多的“良心”小说站点提供的书源来作为…...
数据结构 - 5(二叉树7000字详解)
一:二叉树的基本概念 1.1树形结构 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 注意&am…...
xshell使用方法(超详细)
一、安装 下载最新版安装即可,不需要做任何配置。 安装完成后输入账号名和邮箱,确认后邮箱会收到一条确认邮件,将里面的链接点开即可免费使用(仅安装后会出现,认证后以后再打开不需要重复操作,如果重新安…...
【数据库系统概论】第三章关系数据库标准语言SQL
选择题会考: 1.数据查询: SELECT:用于选择需要查询的列和行。 FROM:用于指定要查询的表。 WHERE:用于指定查询条件。 GROUP BY:用于按照指定的列对结果进行分组。 HAVING:用于指定分组条件…...
云计算是什么?学习云计算能做什么工作?
很多人经常会问云计算是什么?云计算能干什么?学习云计算能做什么工作?其实我们有很多人并不知道云计算是什么,小知今天来给大家讲讲学习云计算能做什么。 中国的云计算行业目前正处于快速发展阶段,随着互联网和数字化…...
ES6 -- 模块化(CommonJS、AMD、ES Module)
模块模式 将代码拆分成独立的块,然后再将这些块连接起来可以通过模块模式来实现。这种模式背后的思想很简单:把逻辑分块,各自封装,相互独立,每个块自行决定对外暴露什么,同时自行决定引入执行哪些外部代码…...
c# xml 参数读取读取的简单使用
完整使用之测试参数的读取(xml) 保存一个xml文档(如果没有就会生成一个默认的 里面的参数用的是我们默认设置的),之后每次更改里面的某项,然后保存 类似于重新刷新一遍。 这里所用的xml测试参数前面需要加…...
gym原来是这样用的
今天down了一个深度强化学习的程序,但是试来试去总是跑不成功,第一句就出问题了 env gym.make("clusterEnv-v0").unwrapped总是报没有该环境,思想半天,然后发现这是自己写的环境,需要到gym中去注册才能使用…...
百度SEO优化技巧与布局(提升网站排名的5种有效方法)
网站SEO关键词介绍: SEO(SearchEngineOptimization)即搜索引擎优化,是通过一系列技术手段和策略,让网站在搜索引擎中获得更好的排名和流量。关键词是SEO优化的重要组成部分,通过关键词布局合理,…...
文案配音软件哪个好?(适合新手使用)
随着短视频的逐渐普及,视频博主越来越多,所以很多朋友也期待成为视频博主。但是,如果你想成为一个有名的视频博主,你需要在很多层面上比别人做得更好。其中之一就是视频文字的配音。相信大部分人都没有配音的技巧,所以…...
excel映射xml方法
excel映射xml方法 创建xml模板 新建一个文本文件,编写模板并命名为xxx.xml <?xml version"1.0" encoding"UTF-8"?> <root><item ID""><surname></surname><man></man><woman>&…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架
1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...
ZYNQ学习记录FPGA(二)Verilog语言
一、Verilog简介 1.1 HDL(Hardware Description language) 在解释HDL之前,先来了解一下数字系统设计的流程:逻辑设计 -> 电路实现 -> 系统验证。 逻辑设计又称前端,在这个过程中就需要用到HDL,正文…...
