Including a groovy script in another groovy

我读过 如何简单地在另一个 Groovy 脚本中导入一个 Groovy 文件

I want to define common functions in one groovy file and call those functions from other groovy files.

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

这可能吗? 有人能举个例子吗。

161251 次浏览

Groovy 没有类似于典型脚本语言的 import 关键字,这些关键字可以直接包含另一个文件的内容(这里暗示: Groovy 是否提供了一种包含机制?)。由于其面向对象/类的特性,您必须“玩游戏”才能实现这样的功能。一种可能性是使所有实用程序函数都是静态的(因为您说过它们不使用对象) ,然后在执行 shell 的上下文中执行静态导入。然后您可以调用这些方法,比如“全局函数”。< br > 另一种可能性是使用 Binding 对象(http://groovy.codehaus.org/api/groovy/lang/Binding.html) ,同时创建 Shell 并将所有希望绑定到方法的函数绑定(这里的缺点是必须枚举绑定中的所有方法,但也许可以使用反射)。另一个解决方案是在分配给 shell 的委托对象中覆盖 methodMissing(...),这样你就可以基本上使用 map 或者任何你喜欢的方法来完成克劳斯·福尔曼。

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

evaluate(new File("../tools/Tools.groovy"))

把这句话放在你剧本的开头。这将引入一个 groovy 文件的内容(只需用您的 groovy 脚本替换双引号之间的文件名即可)。

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

Another way to do this is to define the functions in a groovy class and parse and add the file to the classpath at runtime:

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

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

例如:

Groovy 文件 MainScript.groovy :

abstract class MainScript extends Script {
def meaningOfLife = 42
}

文件 test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript


println "$meaningOfLife" //works as expected

I think that the best choice is to organize utility things in form of groovy classes, add them to classpath and let main script refer to them via import keyword.

例如:

scripts/DbUtils.groovy

class DbUtils{
def save(something){...}
}

Script/script1.groovy:

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

running script:

cd scripts
groovy -cp . script1.groovy

对于后来者来说,Groovy 现在似乎支持 :load file-path命令,它只是重定向来自给定文件的输入,因此现在包含库脚本是微不足道的。

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

Groovy 可以包含:
: load path/to/another/file 调用 _ fn _ from _ file () ;

我做这个的方法是使用 GroovyShell

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

在 Tomcat 上运行的 Groovy 脚本使用了@grahamparks 和@Snowindy 的组合,并做了一些修改:

工具,太棒了

class Utils {
def doSth() {...}
}

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!

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

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类。
  • A Util class calling the Guava third party library by including it as a Grape dependency (@Grab('com.google.guava:guava:23.0')).
  • Util类可以驻留在子目录中。
  • Util类中的方法传递参数。

其他意见/建议:

  • 始终使用 groovy 类而不是 groovy 脚本来实现 groovy 脚本中的可重用功能。上面的示例使用 Util.groovy 文件中定义的 Util 类。使用 Groovy 脚本实现可重用功能是有问题的。例如,如果使用一个 groovy 脚本,那么 Util 类必须在脚本底部用 new Util()实例化,但最重要的是,它必须放在一个名为 Util.groovy 以外的任何文件中。有关 groovy 脚本和 groovy 类之间差异的更多细节,请参考 脚本与类
  • 在上面的例子中,我使用路径 "${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"而不是 "some/subpackage/Util.groovy"。这将保证 Util.groovy文件总是与 groovy 脚本的位置(example.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 集成机制。事实上,可以使用相同的方法从 Groovy 或 Java 中调用任何 JVM language。一些值得注意的 JVM 语言例子有 Groovy、 Java、 Scala、 JRuby 和 JavaScript (Rhino)。

下面是一个完整的例子,说明如何将一个脚本包含在另一个脚本中。
只要运行 Testmain.groovy 文件
包括解释性的评论,因为我就是这样的好人; ]

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")

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

Groovy 外部脚本

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

Groovy 主脚本

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

Result

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

Groovy 可以像 Java 一样导入其他 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

我同意@Snowindy 的观点,最干净的方法可能是将代码组织到 Groovy 类中。

还可以使用 Groovy 的函数调用语法,方法是 static,然后是 static import。这将为几乎所有的意图和目的提供一个函数。

举个简单的例子:
返回文章页面

import groovy.transform.CompileStatic


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

返回文章页面

import static Foo.dofoo


dofoo()

Execute with groovy bar.groovy