如何在Groovy脚本中包含JAR文件?

56

我有一段 Groovy 脚本,需要使用一个 Jar 包中的库。如何将其添加到类路径(classpath)中?我希望该脚本是可执行的,因此在脚本顶部使用了 #!/usr/bin/env groovy


2
看起来这个问题已经被问过了:https://dev59.com/v3VC5IYBdhLWcg3wjx5d - timdisney
如下所述,最好使用 #!/bin/sh,然后使用您想要的任何参数调用 groovy。 最小化将是 #!/bin/sh\n groovy -cp PATH-OR-JAR $@,以将传递给命令行的所有参数传递给 groovy 命令。 在 Windows 中,它将是 groovy -cp PATH %* - PatS
9个回答

58
#!/usr/bin/env groovy开始的Groovy脚本有一个非常重要的限制 - 不能添加额外的参数。 无法配置classpath,无法使用定义或调试运行Groovy。这不是groovy问题,而是shebang (#!)工作方式的限制 - 所有其他参数都被视为单个参数,因此#!/usr/bin/env groovy -d告诉/usr/bin/env运行命令groovy -d而不是带有d参数的groovy
对于此问题,有一种解决方法,涉及在Groovy脚本中使用bash进行引导
#!/bin/bash                                                                                                                                                                 
//usr/bin/env groovy  -cp extra.jar:spring.jar:etc.jar -d -Dlog4j.configuration=file:/etc/myapp/log4j.xml "$0" $@; exit $?

import org.springframework.class.from.jar
//other groovy code
println 'Hello'

所有的魔法都在前两行发生。第一行告诉我们这是一个bash脚本。bash开始运行并看到第一行。在bash中,#用于注释,//折叠成/,表示根目录。所以bash将运行 /usr/bin/env groovy -cp extra.jar:spring.jar:etc.jar -d -Dlog4j.configuration=file:/etc/myapp/log4j.xml "$0" $@,用我们想要的所有参数启动groovy。"$0"是我们的脚本路径,$@是参数。现在groovy运行并忽略前两行,然后退出并返回bash。然后bash退出(exit $?1),带有来自groovy的状态代码。

这个能用Windows批处理文件完成吗? - djangofan
2
不错的提示 - 尽管在 Windows Cygwin shell 中似乎无法正常工作,因为 //usr/bin/env 中的双斜杠没有被折叠。 - Henry
4
在Linux上(在BSD上需要更多的逻辑才能执行readlink -f命令),以下代码将获取当前脚本路径并将其包含在类路径中。// 2>/dev/null; SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"; exec groovy -cp "$SCRIPT_DIR" "$0" - foozbar

39

如果您确实需要,也可以在运行时加载JAR文件:

this.getClass().classLoader.rootLoader.addURL(new File("file.jar").toURL())

1
我无法让那个类加载器工作...还有其他人能使用它吗? - Zombies
4
当使用shebang(#!)运行时,有一些文档没有涵盖的限制。我在这里进一步解释了它们。@Zombies,类加载器技术只适用于您不需要从那个.jar文件中导入类的情况。导入是在脚本运行之前解析和导入的。 - Patrick
1
很遗憾,该链接不再可用。 - Calon
链接已失效,可以尝试访问:https://docs.gradle.org/current/userguide/dependency_management.html#sub:file_dependencies - Hugo Zaragoza
3
对我来说不起作用。java.lang.NullPointerException:无法在null对象上调用addURL()方法。 - fafnir1990
显示剩余4条评论

26

我最喜欢的方法是使用Groovy Grapes。它们可以访问Maven Central Repository,下载所引用的jar包,然后将其放置在类路径上。接下来,您可以像使用其他库一样使用该库。语法非常简单:

@Grab(group='com.google.collections', module='google-collections', version='1.0')

你可以在这里阅读更多详细信息。这种方法的一个主要优点是,当你分发脚本时,不需要分发依赖项。这种方法唯一的缺点是JAR必须在Maven存储库中。


这里的链接已经失效。 - Leo

25

您可以将这些jar包添加到$HOME/.groovy/lib目录下。


适用于我(使用Groovy 2.1.2和Windows 7) - pinei
这很棒,但当你运行了太多的Groovy进程以至于耗尽了打开文件限制时就不再那么好了。 - Sridhar Sarnobat

10

你也可以尝试使用Groovy Grape。它允许您使用注解来修改类路径。目前处于实验阶段,但非常酷。请参见docs.groovy-lang.org/.../grape


6
以下是Patrick的解决方案Maarteen Boekhold的解决方案以及foozbar的评论,可以在Linux和Cygwin上使用的组合方法:
#!/bin/bash
// 2>/dev/null; SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
// 2>/dev/null; OPTS="-cp $SCRIPT_DIR/lib/extra.jar:$SCRIPT_DIR/lib/spring.jar"
// 2>/dev/null; OPTS="$OPTS -d"
// 2>/dev/null; OPTS="$OPTS -Dlog4j.configuration=file:/etc/myapp/log4j.xml"
// 2>/dev/null; exec groovy $OPTS "$0" "$@"; exit $?

import org.springframework.class.from.jar
//other groovy code
println 'Hello'

工作原理:

  • // 是有效的 groovy 注释,因此所有 bash 命令都被 Groovy 忽略。
  • // 会返回错误,但错误输出被重定向到 /dev/null,因此不会显示。
  • 即使前一个命令失败,bash 也会执行分号后面的命令。
  • exec 替换当前进程中的当前程序,而不是分叉出一个新进程。因此,groovy 在原始脚本进程内运行(ps 显示的进程为脚本而不是 groovy 可执行文件)。
  • exec groovy 后面的 exit $? 语句防止 bash 尝试将剩余的脚本解释为 bash 脚本,并保留 groovy 脚本的返回代码。

上述 bash 技巧在某些情况下比 RootLoader 技巧 更方便,因为您可以在脚本中使用常规导入语句。使用 RootLoader 技巧会强制您使用反射加载所有类。这在某些情况下很好(例如当您需要加载 JDBC 驱动程序时),但在其他情况下不方便。

如果您知道您的脚本永远不会在Cygwin上执行,那么使用Patrick或Maarteen的解决方案可能会导致稍微更好的性能,因为它们避免了生成和丢弃错误的开销。


6

和在Java中一样。

这是运行MySQL状态监控脚本的示例。mysql.jar包含了MySQL连接器,我从脚本status.groovy中调用它。

groovy -cp mysql.jar status.groovy ct1


在Windows上,我使用-cp mysql.jar无法正常工作。相反,我必须使用-cp ./mysql.jar - Kirill

3

除了@Patrick提供的非常有用的答案外,我最近发现了另一个技巧。

如果你在一行中添加了许多JAR包到类路径中,事情可能会变得非常难以阅读。但是你可以这样做!

#!/bin/bash
//bin/true && OPTS="-cp blah.jar -Dmyopt=value"
//bin/true && OPTS="$OPTS -Dmoreopts=value2"
//usr/bin/env groovy $OPTS "$0" $@; exit $?

println "inside my groovy script"

让你的想象力自由发挥,想象一下你可以将多么复杂的命令行分解成易于处理的部分。

马尔滕


2
如果你想在import声明之前立即使用它,可以这样做:):

// printEmployees.groovy
this.class.classLoader.rootLoader.addURL(
   new URL("file:///C:/app/Dustin/product/11.1.0/db_1/jdbc/lib/ojdbc6.jar"))
import groovy.sql.Sql
sql = Sql.newInstance("jdbc:oracle:thin:@localhost:1521:orcl", "hr", "hr",
                      "oracle.jdbc.pool.OracleDataSource")
sql.eachRow("SELECT employee_id, last_name, first_name FROM employees")
{
   println "The employee's name is ${it.first_name} ${it.last_name}."
}

Taken from this javaworld.com article.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接