1. 快速入门
1.1. 添加依赖
Grape 是嵌入到 Groovy 中的 JAR 依赖管理工具。Grape 允许您快速将 Maven 仓库依赖项添加到您的类路径中,从而使脚本编写更加轻松。最简单的用法就像在您的脚本中添加一个注解一样简单
@Grab(group='org.springframework', module='spring-orm', version='5.2.8.RELEASE')
import org.springframework.jdbc.core.JdbcTemplate
@Grab
也支持简写符号
@Grab('org.springframework:spring-orm:5.2.8.RELEASE')
import org.springframework.jdbc.core.JdbcTemplate
请注意,我们在这里使用的是带注解的导入,这是推荐的方式。您也可以在 mvnrepository.com 上搜索依赖项,它将为您提供 pom.xml
条目的 @Grab
注解形式。
1.2. 指定其他仓库
并非所有依赖项都位于 Maven 中央仓库。您可以像这样添加新的依赖项
@GrabResolver(name='restlet', root='http://maven.restlet.org/')
@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
1.3. Maven 分类器
一些 Maven 依赖项需要分类器才能解析。您可以像这样修复它
@Grab(group='net.sf.json-lib', module='json-lib', version='2.2.3', classifier='jdk15')
1.4. 排除传递依赖
有时您需要排除传递依赖,因为您可能已经在使用某个工件的稍微不同但兼容的版本。您可以按照以下步骤操作
@Grab('net.sourceforge.htmlunit:htmlunit:2.8')
@GrabExclude('xml-apis:xml-apis')
1.5. JDBC 驱动程序
由于 JDBC 驱动程序的加载方式,您需要将 Grape 配置为将 JDBC 驱动程序依赖项附加到系统类加载器。例如
@GrabConfig(systemClassLoader=true)
@Grab(group='mysql', module='mysql-connector-java', version='5.1.6')
1.6. 从 Groovy Shell 使用 Grape
从 groovysh 使用方法调用变体
groovy.grape.Grape.grab(group:'org.springframework', module:'spring', version:'2.5.6')
1.7. 代理设置
如果您在防火墙后面,或者需要通过代理服务器使用 Groovy/Grape,您可以在命令行中通过 http.proxyHost
和 http.proxyPort
系统属性指定这些设置
groovy -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080 yourscript.groovy
或者,您也可以通过将这些属性添加到您的 JAVA_OPTS 环境变量中来使其在系统范围内生效
JAVA_OPTS = -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080
1.8. 日志记录
如果您想查看 Grape 在做什么,请将系统属性 groovy.grape.report.downloads
设置为 true
(例如,将 -Dgroovy.grape.report.downloads=true
添加到调用或 JAVA_OPTS 中),Grape 将打印以下信息到 System.error
-
开始解析依赖项
-
开始下载工件
-
重试下载工件
-
已下载工件的下载大小和时间
要以更多详细程度记录日志,请增加 Ivy 日志级别(默认值为 -1
)。例如 -Divy.message.logger.level=4
。
2. 详细
Grape(Groovy Adaptable Packaging Engine 或 Groovy Advanced Packaging Engine)是支持 Groovy 中 grab() 调用的基础设施,它是一组利用 Ivy 的类,为 Groovy 提供基于仓库的模块系统。这允许开发人员编写一个脚本,该脚本具有基本上任意的库要求,并且只发布脚本。Grape 将在运行时根据需要下载和链接命名库以及所有依赖项,形成一个传递闭包,当脚本从现有的仓库(如 Maven 中央仓库)运行时,这些依赖项将被链接。
Grape 遵循 Ivy 用于模块版本识别的约定,但名称有所更改。
-
group
- 模块所属的模块组。直接转换为 Maven groupId 或 Ivy Organization。任何与/groovy[x][\..*]^/
匹配的组都是保留的,并且可能对 groovy 支持的模块具有特殊含义。 -
module
- 要加载的模块的名称。直接转换为 Maven artifactId 或 Ivy artifact。 -
version
- 要使用的模块的版本。可以是文字版本 `1.1-RC3` 或 Ivy 范围 `[2.2.1,)`,表示 2.2.1 或任何更高版本。 -
classifier
- 可选的分类器(例如,jdk15)
下载的模块将根据 Ivy 的标准机制存储,缓存根目录为 ~/.groovy/grapes
3. 用法
3.1. 注解
可以在任何接受注解的地方添加一个或多个 groovy.lang.Grab
注解,以告知编译器此代码依赖于特定的库。这将使库被添加到 groovy 编译器的类加载器中。此注解在解析脚本中的其他类之前被检测和评估,因此导入的类可以由 @Grab
注解正确解析。
import com.jidesoft.swing.JideSplitButton
@Grab(group='com.jidesoft', module='jide-oss', version='[2.2.1,2.3.0)')
public class TestClassAnnotation {
public static String testMethod () {
return JideSplitButton.class.name
}
}
一个适当的 grab(…)
调用将被添加到包含类的类的静态初始化器中(或对于带注解的脚本元素,则添加到脚本类中)。
3.2. 多个 Grape 注解
在早期版本的 Groovy 中,如果您想在同一个节点上多次使用 Grab 注解,则必须使用 @Grapes
注解,例如
@Grapes([
@Grab(group='commons-primitives', module='commons-primitives', version='1.0'),
@Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='0.9.7')])
class Example {
// ...
}
否则,您将遇到以下错误
Cannot specify duplicate annotation on the same member
但在最新版本中,@Grapes 是完全可选的。
技术说明
-
最初,Groovy 存储 Grab 注解以在运行时访问,并且字节码中不允许重复。在当前版本中,@Grab 只有 SOURCE 保留,因此多次出现不是问题。
-
未来版本的 Grape 可能支持使用 Grapes 注解来提供结构化级别,例如,允许 GrabExclude 或 GrabResolver 注解仅应用于 Grab 注解的子集。
3.3. 方法调用
通常,对 grab 的调用会在脚本的早期或类初始化时发生。这是为了确保在 groovy 代码依赖于代码之前,库可供 ClassLoader 使用。以下是一些典型的调用方式
import groovy.grape.Grape
// random maven library
Grape.grab(group:'com.jidesoft', module:'jide-oss', version:'[2.2.0,)')
Grape.grab([group:'org.apache.ivy', module:'ivy', version:'2.0.0-beta1', conf:['default', 'optional']],
[group:'org.apache.ant', module:'ant', version:'1.7.0'])
-
在同一上下文中使用相同参数对 grab 进行多次调用应该是幂等的。但是,如果在不同的
ClassLoader
上下文中调用相同的代码,则可能会重新运行解析。 -
如果传递给
grab
调用的args
映射中有一个名为noExceptions
的属性,并且该属性的值为 true,则不会抛出异常。 -
grab
要求指定RootLoader
或GroovyClassLoader
,或者在调用类的ClassLoader
链中存在这些类。默认情况下,如果无法获得这样的ClassLoader
,将导致模块解析失败,并抛出异常-
通过
classLoader:
参数传递的 ClassLoader 及其父类加载器。 -
作为
referenceObject:
参数传递的对象的类的最近的父类加载器,以及它的父类加载器。 -
发出对
grab
的调用的类的 ClassLoader
-
3.3.1. grab(HashMap) 参数
-
group:
- <String> - 模块所属的模块组。直接转换为 Maven groupId。任何与/groovy(|\..|x|x\..)/
匹配的组都是保留的,并且可能对 groovy 支持的模块具有特殊含义。 -
module:
- <String> - 要加载的模块的名称。直接转换为 Maven artifactId。 -
version:
- <String> 以及可能的 <Range> - 要使用的模块的版本。可以是文字版本 `1.1-RC3` 或 Ivy 范围 `[2.2.1,)`,表示 2.2.1 或任何更高版本。 -
classifier:
- <String> - 要通过它解析的 Maven 分类器。 -
conf:
- <String>,默认值为default' - 要下载的模块的配置或范围。默认配置为 `default:
,它映射到 maven 的 `runtime` 和 `master` 范围。 -
force:
- <boolean>,默认为 true - 用于指示在发生冲突的情况下必须使用此修订版,与 -
冲突管理器
-
changing:
- <boolean>,默认为 false - 是否可以在不改变其版本标识的情况下改变工件。 -
transitive:
- <boolean>,默认为 true - 是否解析此模块拥有的其他依赖项。
grab
主要有两种变体,一种使用单个映射,另一种使用参数映射和多个依赖项映射。调用单映射 grab 与使用相同映射两次调用 grab 相同,因此 grab 参数和依赖项可以在同一映射中混合使用,并且 grab 可以作为具有命名参数的单个方法调用。
这些参数有同义词。提交多个同义词会导致运行时异常。
-
group:
,groupId:
,organisation:
,organization:
,org:
-
module:
,artifactId:
,artifact:
-
version:
,revision:
,rev:
-
conf:
,scope:
,configuration:
3.3.2. 参数映射参数
-
classLoader:
- <GroovyClassLoader> 或 <RootClassLoader> - 要将已解析的 Jar 添加到的 ClassLoader -
refObject:
- <Object> - 对象类的最近的父类加载器将被视为已作为classLoader:
传入。 -
validate:
- <boolean>,默认为 false - 是否应该验证 pom 或 ivy 文件(true),还是应该信任缓存(false)。 -
noExceptions:
- <boolean>,默认为 false - 如果 ClassLoader 解析或仓库查询失败,是否应该抛出异常(false)还是静默失败(true)。
3.4. 命令行工具
Grape 添加了一个命令行可执行文件 `grape`,它允许检查和管理本地 grape 缓存。
grape install [-hv] <group> <module> [<version>] [<classifier>]
这将安装指定的 groovy 模块或 maven 工件。如果指定了版本,则将安装该特定版本,否则将使用最新版本(就像传递了 `*` 一样)。
grape list
列出本地安装的模块(对于 groovy 模块,使用它们的完整 maven 名称)及其版本。
grape resolve [-adhisv] (<groupId> <artifactId> <version>)+
这将返回表示指定模块及其相应传递依赖项的工件的 jar 文件的位置。您可以选择传递 -ant、-dos 或 -shell 以获得适用于 ant 脚本、windows 批处理文件或 unix shell 脚本的格式的依赖项。可以传递 -ivy 以查看以类似 ivy 的格式表示的依赖项。
grape uninstall [-hv] <group> <module> <version>
这将卸载特定的 grape:它非传递性地从 grape 缓存中删除相应的 jar 文件。
3.5. 高级配置
3.5.1. 仓库目录
如果您需要更改 grape 用于下载库的目录,您可以指定 grape.root 系统属性来更改默认目录(默认为 ~/.groovy/grapes)
groovy -Dgrape.root=/repo/grapes yourscript.groovy
3.5.2. 自定义 Ivy 设置
您可以通过创建一个 ~/.groovy/grapeConfig.xml 文件来自定义 Grape 使用的 Ivy 设置。如果不存在此文件,这里 是 Grape 使用的默认设置。
有关如何自定义这些设置的更多信息,请参阅 Ivy 文档。
3.6. 更多示例
使用 Apache Commons Collections
// create and use a primitive array list
@Grab(group='commons-primitives', module='commons-primitives', version='1.0')
import org.apache.commons.collections.primitives.ArrayIntList
def createEmptyInts() { new ArrayIntList() }
def ints = createEmptyInts()
ints.add(0, 42)
assert ints.size() == 1
assert ints.get(0) == 42
使用 TagSoup
// find the PDF links of the Java specifications
@Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='1.2.1')
def getHtml() {
def parser = new XmlParser(new org.ccil.cowan.tagsoup.Parser())
parser.parse("https://docs.oracle.com/javase/specs/")
}
html.body.'**'[email protected](~/.*\.pdf/).each{ println it }
使用 Google Collections
import com.google.common.collect.HashBiMap
@Grab(group='com.google.code.google-collections', module='google-collect', version='snapshot-20080530')
def getFruit() { [grape:'purple', lemon:'yellow', orange:'orange'] as HashBiMap }
assert fruit.lemon == 'yellow'
assert fruit.inverse().yellow == 'lemon'
启动 Jetty 服务器以提供 Groovy 模板
@Grab('org.eclipse.jetty.aggregate:jetty-server:8.1.19.v20160209')
@Grab('org.eclipse.jetty.aggregate:jetty-servlet:8.1.19.v20160209')
@Grab('javax.servlet:javax.servlet-api:3.0.1')
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.ServletContextHandler
import groovy.servlet.TemplateServlet
def runServer(duration) {
def server = new Server(8080)
def context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS)
context.resourceBase = "."
context.addServlet(TemplateServlet, "*.gsp")
server.start()
sleep duration
server.stop()
}
runServer(10000)
Grape 将在第一次启动此脚本时下载 Jetty 及其依赖项,并将其缓存。我们创建了一个新的 Jetty 服务器,端口为 8080,然后在上下文的根目录中公开 Groovy 的 TemplateServlet——Groovy 自带强大的模板引擎机制。我们启动服务器并让它运行一段时间。每次有人访问 http://localhost:8080/somepage.gsp 时,它将向用户显示 somepage.gsp 模板——这些模板页面应与此服务器脚本位于同一个目录中。