在另一个 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
【讨论】:
我想知道如果你有像lib
和src
目录这样的目录结构,这将如何工作【参考方案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.groovy
file1.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 脚本的主要内容,如果未能解决你的问题,请参考以下文章
Grails:有条件地加载 Spring Security LDAP 插件