百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

闲谈Groovy使用

csdh11 2025-05-23 16:23 4 浏览

groovy

Groovy是一门基于JVM的动态语言,同时也是一门面向对象的语言,语法上和Java非常相似。它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。 Java作为一种通用、静态类型的编译型语。

相对于Java,它在编写代码的灵活性上有非常明显的提升,对于一个长期使用Java的开发者来说,使用Groovy时能够明显地感受到负身上的“枷锁”轻了。Groovy是动态编译语言,广泛用作脚本语言和快速原型语言,主要优势之一就是它的生产力。Groovy 代码通常要比 Java 代码更容易编写,而且编写起来也更快,这使得它有足够的资格成为开发工作包中的一个附件。

Java不是解决动态层问题的理想语言,这些动态层问题包括原型设计、脚本处理等。可以把Groovy看作给Java静态世界补充动态能力的语言,同时Groovy已经实现了java不具备的语言特性:

  • 函数字面值;
  • 对集合的一等支持;
  • 对正则表达式的一等支持;
  • 对xml的一等支持;

QLExpress和Groovy对比

  • 两者性能相差不多。
  • QLExpress是解析型,编译成自定义的内存指令。而Groovy是编译形,产生一个独立的class文件。
  • 两者都是表达式语言(EL expression language) ,同样是弱类型
  • Groovy更兼容java语法,QLExpress更强调功能扩展。
  • QLExpress更简洁,操作系统以及应用领域更广。

groovy与java集成

Groovy调用Java方式包括GroovyClassLoader、GroovyShell和GroovyScriptEngine。

GroovyClassLoader

用 Groovy 的 GroovyClassLoader ,动态地加载一个脚本并执行它的行为。GroovyClassLoader是一个定制的类装载器,负责解释加载Java类中用到的Groovy类。

GroovyClassLoader loader = new GroovyClassLoader();
Class groovyClass = loader.parseClass(new File(groovyFileName));
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
groovyObject.invokeMethod("run", "helloworld");

GroovyShell

GroovyShell允许在Java类中(甚至Groovy类)求任意Groovy表达式的值。您可使用Binding对象输入参数给表达式,并最终通过GroovyShell返回Groovy表达式的计算结果。

GroovyShell shell = new GroovyShell();
Script groovyScript = shell.parse(new File(groovyFileName));
Object[] args = {};
groovyScript.invokeMethod("run", args);

GroovyScriptEngine

GroovyShell多用于推求对立的脚本或表达式,如果换成相互关联的多个脚本,使用GroovyScriptEngine会更好些。GroovyScriptEngine从指定的位置(文件系统,URL,数据库,等等)加载Groovy脚本,并且随着脚本变化而重新加载它们。如同GroovyShell一样,GroovyScriptEngine也允许您传入参数值,并能返回脚本的值。

Class script = new GroovyScriptEngine(filePath)
                .loadScriptByName(fileName);

Script instance =(Script) script.newInstance();
            instance.invokeMethod (scriptName,new Object[]{});

Groovy代码文件与class文件的对应关系

而作为基于JVM的语言,Groovy可以非常容易的和Java进行互操作,但也需要编译成class文件后才能运行,所以了解Groovy代码文件和class文件的对应关系,有助于更好地理解Groovy的运行方式和结构。

对于没有任何类定义

如果Groovy脚本文件里只有执行代码,没有定义任何类(class),则编译器会生成一个Script的子类,类名和脚本文件的文件名一样,而脚本的代码会被包含在一个名为run的方法中,同时还会生成一个main方法,作为整个脚本的入口。

对于仅有一个类

如果Groovy脚本文件里仅含有一个类,而这个类的名字又和脚本文件的名字一致,这种情况下就和Java是一样的,即生成与所定义的类一致的class文件。

对于多个类

如果Groovy脚本文件含有多个类,groovy编译器会很乐意地为每个类生成一个对应的class文件。如果想直接执行这个脚本,则脚本里的第一个类必须有一个static的main方法。

GroovyShell的parse方法导致perm区爆满问题

如果应用中内嵌Groovy引擎,会动态执行传入的表达式并返回执行结果,而Groovy每执行一次脚本,都会生成一个脚本对应的class对象,并new一个InnerLoader去加载这个对象,而InnerLoader和脚本对象都无法在gc的时候被回收运行一段时间后将perm占满,一直触发fullgc。

Groovy每执行一次脚本,都会生成一个脚本对应的class对象

一个ClassLoader对于同一个名字的类只能加载一次,都由GroovyClassLoader加载,那么当一个脚本里定义了C这个类之后,另外一个脚本再定义一个C类的话,GroovyClassLoader就无法加载了。

每次执行都会加载

这是因为对于同一个groovy脚本,groovy执行引擎都会不同的命名,且命名与时间戳有关系。当传入text时,class对象的命名规则为:"script" + System.currentTimeMillis() + Math.abs(text.hashCode()) + ".groovy"。这就导致就算groovy脚本未发生任何变化,每次执行parse方法都会新生成一个脚本对应的class对象,且由GroovyClassLoader进行加载,不断增大perm区。

InnerLoader加载的对应无法通过gc清理掉

JVM中的Class只有满足以下三个条件,才能被GC回收

  • 该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例
  • 加载该类的ClassLoader已经被GC
  • 该类的java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法

GroovyClassLoader代码中有一个class对象的缓存,每次编译脚本时都会在Map中缓存该对象,即:setClassCacheEntry(clazz)。每次groovy编译脚本后,都会缓存该脚本的Class对象,下次编译该脚本时,会优先从缓存中读取,这样节省掉编译的时间。

这个缓存的Map由GroovyClassLoader持有,key是脚本的类名,这就导致每个脚本对应的class对象都存在引用,无法被gc清理掉。

解决方法

  • GroovyClassLoader类有clearCache可以清除缓存。
  • 缓存Script对象,每次先去缓存中获取。
  • 第二种方法,通常又会引入新的问题:

    但是如此处理的话,通常又会引入新的问题:

    高并发情况下,binding对象混乱导致计算出错

    在高并发的情况下,在执行赋值binding对象后,真正执行run操作时,拿到的binding对象可能是其它线程赋值的对象,所以出现数据计算混乱的情况。

    建议每个 script 都 new 一个 GroovyClassLoader 来装载。

    长时间运行仍然出现oom,无法解决Class

    由于groovyClassLoader会缓存每次编译groovy脚本的Class对象,下次编译该脚本时,会优先从缓存中读取,这样节省掉编译的时间。导致被加载的Class对象因为存在引用而无法被卸载,虽然通过缓存避免了短时间内大量生成新的class对象,但如果长时间运营仍然会存在问题。

    建议对于 parseClass 后生成的 Class 对象进行cache,key 为 groovyScript 脚本的md5值。


    相关推荐

    跨越26年的宝可梦系列游戏你玩过几款?

    作为一个宝可梦IP的忠实爱好者,是时时刻刻关注着宝可梦的影视及游戏方面,在22年2月也即将引来正统续作宝可梦阿尔宙斯(其实还是更习惯口袋妖怪的翻译)宝可梦从1996年诞生至今一共有多少正统系列,小编做...

    ndsi,肩键L键失灵,拆开看看能不能修

    很久没有玩nds了,新买了个R4烧录卡是带64g的游戏来玩玩,发现肩键L失灵了,打开看看能不能挽救一下。之前后盖上是有痛贴的,没办法只能忍痛撕下来了,撕下来之后还真白啊,上保护贴是真有必要的。话不多说...

    R4卡使用方法!游戏介绍及常见问题!任天堂掌机通用

    哈喽大家好!我是掌机百科!今天介绍下R4卡的使用方法及教程,DS,NDS,NDSL,NDSLL,NDSXL,NDSI.NDSILL,NDSIXL,3DS,3DSLL,3DSXL,2DS,NEW3DS...

    JavaScript简介:从概念、特点、组成和用法全面带你快速了解JS

    “这里是云端源想IT,帮你轻松学IT”...

    FileBrowser 是一个优秀的基于 Web 的文件管理器

    FileBrowser是一个基于网页的文件管理器,允许用户通过浏览器界面管理服务器上的文件和目录。它提供了一种简单直观的方式来执行文件操作,如上传、下载、删除、重命名和编辑文件,以及管理目录。以下是...

    BrowserTools MCP赋能Cursor IDE:基于上下文实现浏览器无缝控制

    一、项目介绍BrowserToolsMCP是基于Anthropic的ModelContextProtocol(MCP)构建的浏览器交互工具,通过与CursorIDE等AI编辑器深度整合,实现...

    正确复制、重写别人的代码,不算抄袭

    我最近在一篇文章提到,工程师应该怎样避免使用大量的库、包以及其他依赖关系。我建议的另一种方案是,如果你没有达到重用第三方代码的阈值时,那么你就可以自己编写代码。...

    它来了、全新的Vue3中文文档来了

    Vue.js-渐进式JavaScript框架|Vue.jsPlayicon...

    “我们太乙变化大”——第一书记讲述贫困村变化

    来源:江西日报-江西新闻客户端江西新闻客户端讯(江西日报记者唐燕)“2018年1月,组织安排我到太乙村开展帮扶工作。两年来,我亲眼目睹并亲身经历了群众生产生活发生的巨大变化。看到群众生活越来越幸福,我...

    趋势科技杀软曝密码漏洞:允许远程代码执行

    IT之家讯来自谷歌的安全研究员TavisOrmandy最近很忙。几周之前,他发现了AVG杀软扩展程序可以绕过Chrome商店中的审查机制,自动安装到用户的Chrome浏览器中,而且这个扩展还会泄露...

    Vue3安装使用方式详解

    对于开发和学习任何一门新技术,新框架,最主要的还是能够运行起来,怎么运行起来,这才是最主要的。所以,我们来说一下Vue.js的安装方式。因为Vue.js的设计初始,就是一个渐进式的J...

    设置 Cursor MCP 与 BrowserTools 服务

    一、安装Chrome扩展打开Chrome网上应用店,搜索...

    那些通用清除软件不曾注意的秘密

    系统清理就像卫生检查前的大扫除,即使你使出吃奶的劲儿把一切可能的地方都打扫过,还会留下边边角角的遗漏。随着大家电脑安全意识的提高,越来越多的朋友开始关注自己的电脑安全,也知道安装360系列软件来"武装...

    SharpBrowser:用C#打造超快的个性化开源浏览器!

    推荐一个基于.Net8和CefSharp开发的开源浏览器。项目简介SharpBrowser是一个用C#和CefSharp开发的全功能网页浏览器。它声称是最快的开源C#网页浏览器,渲...

    微软发布轻量级 VS Code 工具,可完全在浏览器中运行

    ...