在另一个 groovy 中包含一个 groovy 脚本

Posted

技术标签:

【中文标题】在另一个 groovy 中包含一个 groovy 脚本【英文标题】:Including a groovy script in another groovy 【发布时间】:2012-02-26 12:34:41 【问题描述】:

我已阅读how to simply import a groovy file in another groovy script

我想在一个 groovy 文件中定义常用函数,并从其他 groovy 文件中调用这些函数。

我知道这将像使用脚本语言一样使用 Groovy,即我不需要类/对象。我正在尝试可以在 groovy 中完成的类似 dsl 的东西。所有变量都将从 Java 断言,我想在 shell 中执行 groovy 脚本。

这可能吗?谁能举个例子。

【问题讨论】:

Load script from groovy script的可能重复 【参考方案1】:
evaluate(new File("../tools/Tools.groovy"))

将它放在脚本的顶部。这将引入一个 groovy 文件的内容(只需将双引号之间的文件名替换为您的 groovy 脚本)。

我用一个名为“Tools.groovy”的类来做这件事。

【讨论】:

文件名需要符合 Java 的类命名规则才能工作。 问题 - 如何将参数传递给我正在使用此语法评估的脚本? @steve 你不能,但你可以在那个脚本中定义一个函数,你可以用参数调用它 它不起作用...脚本经过良好评估,但调用方范围内不存在声明(def、类等) 您必须从调用一返回一个对象,然后将评估结果分配给一个变量。【参考方案2】:

从 Groovy 2.2 开始,可以使用新的 @BaseScript AST 转换注解声明基脚本类。

例子:

文件 MainScript.groovy

abstract class MainScript extends Script 
    def meaningOfLife = 42

文件 test.groovy

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected

【讨论】:

使用此方法时,我不断收到“无法解析类”。你会建议我做什么?有没有办法将自定义类导入另一个 groovy 脚本? 我不知道它是如何工作的。您在哪里提供MainScript.groovy 的路径?【参考方案3】:

另一种方法是在 groovy 类中定义函数并在运行时解析并将文件添加到类路径:

File sourceFile = new File("path_to_file.groovy");
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
GroovyObject myObject = (GroovyObject) groovyClass.newInstance();

【讨论】:

这个解决方案实际上最适合我。当我尝试使用接受的答案时,我收到一条错误消息,指出我的主要 groovy 脚本无法解析评估脚本中定义的类。为了它的价值...... 我尝试了几种在 SO 上发布的不同方法,但只有这种方法有效。其他人抛出了关于无法解析类或方法的错误。这是我使用的版本 Groovy 版本:2.2.2 JVM:1.8.0 供应商:Oracle Corporation 操作系统:Windows 7。 这很好用。请务必明确使用GroovyObject,这不是您自己的类名的占位符。 我仍然得到:java.lang.NoClassDefFoundError: groovy.lang.GroovyObject 救命稻草。谢谢老哥!!【参考方案4】:

我认为最好的选择是以 groovy 类的形式组织实用程序,将它们添加到类路径并让主脚本通过 import 关键字引用它们。

例子:

脚本/DbUtils.groovy

class DbUtils
    def save(something)...

脚本/script1.groovy:

import DbUtils
def dbUtils = new DbUtils()
def something = 'foobar'
dbUtils.save(something)

运行脚本:

cd scripts
groovy -cp . script1.groovy

【讨论】:

我想知道如果你有像libsrc 目录这样的目录结构,这将如何工作【参考方案5】:

我这样做的方式是使用GroovyShell

GroovyShell shell = new GroovyShell()
def Util = shell.parse(new File('Util.groovy'))
def data = Util.fetchData()

【讨论】:

【参考方案6】:

如何将外部脚本视为 Java 类?基于这篇文章:https://www.jmdawson.net/blog/2014/08/18/using-functions-from-one-groovy-script-in-another/

getThing.groovy 外部脚本

def getThingList() 
    return ["thing","thin2","thing3"]

printThing.groovy主脚本

thing = new getThing()  // new the class which represents the external script
println thing.getThingList()

结果

$ groovy printThing.groovy
[thing, thin2, thing3]

【讨论】:

如果您在源存储库之外运行脚本将无法工作 如果 getThing.groovy 在另一个文件夹中怎么办?【参考方案7】:

这是在另一个脚本中包含一个脚本的完整示例。 只需运行 Testmain.groovy 文件 包括解释性 cmets,因为我很喜欢那样 ;]

Testutils.groovy

// This is the 'include file'
// Testmain.groovy will load it as an implicit class
// Each method in here will become a method on the implicit class

def myUtilityMethod(String msg) 
    println "myUtilityMethod running with: $msg"

Testmain.groovy

// Run this file

// evaluate implicitly creates a class based on the filename specified
evaluate(new File("./Testutils.groovy"))
// Safer to use 'def' here as Groovy seems fussy about whether the filename (and therefore implicit class name) has a capital first letter
def tu = new Testutils()
tu.myUtilityMethod("hello world")

【讨论】:

【参考方案8】:

Groovy 没有像典型脚本语言那样的 import 关键字,它会直接包含另一个文件的内容(这里提到:Does groovy provide an include mechanism?)。 由于其面向对象/类的性质,您必须“玩游戏”才能使这样的事情发挥作用。一种可能性是使所有实用程序函数成为静态的(因为您说它们不使用对象),然后在执行 shell 的上下文中执行静态导入。然后,您可以将这些方法称为“全局函数”。 另一种可能性是在创建 Shell 并将所需的所有函数绑定到方法时使用 Binding 对象 (http://groovy.codehaus.org/api/groovy/lang/Binding.html)(这里的缺点是必须枚举绑定中的所有方法,但您也许可以使用反射)。另一种解决方案是在分配给您的 shell 的委托对象中覆盖 methodMissing(...),这允许您基本上使用映射或您喜欢的任何方法进行动态调度。

这里演示了其中几种方法:http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/。如果您想查看特定技术的示例,请告诉我。

【讨论】:

此链接现已失效【参考方案9】:

@grahamparks 和 @snowindy 答案的组合以及一些修改对我在 Tomcat 上运行的 Groovy 脚本有效:

Utils.groovy

class Utils 
    def doSth() ...

MyScript.groovy:

/* import Utils --> This import does not work. The class is not even defined at this time */
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(new File("full_path_to/Utils.groovy")); // Otherwise it assumes current dir is $CATALINA_HOME
def foo = groovyClass.newInstance(); // 'def' solves compile time errors!!
foo.doSth(); // Actually works!

【讨论】:

我得到:java.lang.NoClassDefFoundError: groovy.lang.GroovyObject【参考方案10】:

Groovy 可以像 Java 一样导入其他 groovy 类。只要确保库文件的扩展名是 .groovy。

    $ cat lib/Lib.groovy
    package lib
    class Lib 
       static saySomething()  println 'something' 
       def sum(a,b)  a+b 
    

    $ cat app.gvy
    import lib.Lib
    Lib.saySomething();
    println new Lib().sum(37,5)

    $ groovy app
    something
    42

【讨论】:

【参考方案11】:

对于后来者来说,groovy 现在似乎支持:load file-path 命令,该命令只是重定向来自给定文件的输入,因此现在包含库脚本很简单。

它作为 groovysh 的输入和加载文件中的一行:groovy:000> :load file1.groovy

file1.groovy 可以包含::load path/to/another/file invoke_fn_from_file();

【讨论】:

你能详细说明一下吗?这是在文档中的什么地方?我把:load file-path放在哪里? 好吧,它可以作为 groovysh 的输入和加载文件中的一行:groovy:000> :load file1.groovyfile1.groovy 可以包含::load path/to/another/file 我找到了load in the docs。如果我理解正确,它适用于 groovysh? 这不适用于在变量中定义的路径,不是吗?【参考方案12】:

经过一番调查,我得出结论,以下方法似乎是最好的。

some/subpackage/Util.groovy

@GrabResolver(name = 'nexus', root = 'https://local-nexus-server:8443/repository/maven-public', m2Compatible = true)
@Grab('com.google.errorprone:error_prone_annotations:2.1.3')
@Grab('com.google.guava:guava:23.0')
@GrabExclude('com.google.errorprone:error_prone_annotations')

import com.google.common.base.Strings

class Util 
    void msg(int a, String b, Map c) 
        println 'Message printed by msg method inside Util.groovy'
        println "Print 5 asterisks using the Guava dependency $Strings.repeat("*", 5)"
        println "Arguments are a=$a, b=$b, c=$c"
    

example.groovy

#!/usr/bin/env groovy
Class clazz = new GroovyClassLoader().parseClass("$new File(getClass().protectionDomain.codeSource.location.path).parent/some/subpackage/Util.groovy" as File)
GroovyObject u = clazz.newInstance()
u.msg(1, 'b', [a: 'b', c: 'd'])

要运行example.groovy 脚​​本,请将其添加到您的系统路径并从任何目录键入:

example.groovy

脚本打印:

Message printed by msg method inside Util.groovy
Print 5 asterisks using the Guava dependency *****
Arguments are a=1, b=b, c=[a:b, c:d]

上面的例子是在以下环境中测试的:Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

该示例演示了以下内容:

如何在 groovy 脚本中使用 Util 类。 一个Util 类调用Guava 第三方库,将其包含为Grape 依赖项(@Grab('com.google.guava:guava:23.0'))。 Util 类可以驻留在子目录中。 将参数传递给Util 类中的方法。

其他 cmets/建议:

始终使用 groovy 类而不是 groovy 脚本来实现 groovy 脚本中的可重用功能。上面的示例使用 Util.groovy 文件中定义的 Util 类。使用 groovy 脚本来实现可重用功能是有问题的。例如,如果使用 groovy 脚本,则 Util 类必须在脚本底部使用new Util() 进行实例化,但最重要的是,它必须放在一个名为 Util.groovy 的文件中。有关 groovy 脚本和 groovy 类之间差异的更多详细信息,请参阅Scripts versus classes。 在上面的示例中,我使用路径"$new File(getClass().protectionDomain.codeSource.location.path).parent/some/subpackage/Util.groovy" 而不是"some/subpackage/Util.groovy"。这将保证始终可以找到与 groovy 脚本的位置 (example.groovy) 相关的 Util.groovy 文件,而不是当前工作目录。例如,使用"some/subpackage/Util.groovy" 将导致搜索WORK_DIR/some/subpackage/Util.groovy。 遵循 Java 类命名约定来命名您的 groovy 脚本。我个人更喜欢脚本以小写字母而不是大写字母开头的小偏差。例如,myScript.groovy 是脚本名,MyClass.groovy 是类名。命名 my-script.groovy 在某些情况下会导致运行时错误,因为生成的类将没有有效的 Java 类名。 在 JVM 世界中,相关功能通常被命名为 JSR 223: Scripting for the Java。特别是在 groovy 中,该功能被命名为 Groovy integration mechanisms。事实上,可以使用相同的方法从 Groovy 或 Java 中调用任何JVM language。此类 JVM 语言的一些著名示例包括 Groovy、Java、Scala、JRuby 和 javascript (Rhino)。

【讨论】:

【参考方案13】:

我同意@snowindy 的观点,最简洁的方法可能是将代码组织到 Groovy 类。

您还可以使用 Groovy 的函数调用语法,方法是创建方法 static 然后 static import 对它们进行设置。这将为您提供几乎所有意图和目的的功能。

简单示例:Foo.groovy

import groovy.transform.CompileStatic

@CompileStatic
class Foo 
    static def dofoo() 
        println("foo")
    

bar.groovy:

import static Foo.dofoo

dofoo()

使用groovy bar.groovy执行

【讨论】:

以上是关于在另一个 groovy 中包含一个 groovy 脚本的主要内容,如果未能解决你的问题,请参考以下文章

蚂蚁如何在groovy中使用

Grails:有条件地加载 Spring Security LDAP 插件

声纳:在一个项目中索引了 0 个文件,在另一个项目中正确扫描

是否可以在另一个中包含一个 CSS 文件?

在另一个数组中包含一个数组

如何在另一个 JavaScript 文件中包含一个 JavaScript 文件?