Grape 依赖管理
1. 快速入门
1.1. 添加依赖项
Grape 是一个 JAR 依赖管理,嵌入在 Groovy 中。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 central。你可以这样添加新的依赖项
@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 外壳使用 Grape
从 groovysh 使用方法调用变量
groovy.grape.Grape.grab(group:'org.springframework', module:'spring', version:'2.5.6')
1.7. 代理设置
如果您处于防火墙后面和/或需要通过代理服务器使用 Groovy/Grape,则可以通过 和
系统属性在命令中指定这些设置
groovy -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080 yourscript.groovy
或者,您可以通过将这些属性添加到 JAVA_OPTS 环境变量来实现系统范围的应用
JAVA_OPTS = -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080
1.8. 日志记录
如果您想查看 Grape 正在执行的操作,请将系统属性 设置为
(例如,将
添加到调用或 JAVA_OPTS),然后 Grape 将以下信息打印到 System.error
-
启动依赖项解析
-
启动工件下载
-
重试工件下载
-
下载工件的下载大小和时间
要以更高的详细程度记录日志,请提高 Ivy 日志级别(默认为 )。例如
。
2. 详细信息
Grape(Groovy 适应性打包引擎或Groovy 高级打包引擎)是启用 Groovy 中 grab() 调用的基础设施,它是一组利用 Ivy 的类,可以为 Groovy 提供一个由存储库驱动的模块系统。这使开发人员能够编写对任意库要求的脚本,然后仅发送该脚本。当脚本从现有的存储库(如 Maven Central)运行时,Grape 将根据需要下载并链接已命名的库及其所有依赖项,形成传递闭包。
Grape 遵循 Ivy 的模块版本标识规范,并对命名进行了更改。
-
group
- 模块所属的模块组。直接转换为 Maven groupId 或 Ivy 组织。与匹配的任何组都是保留的,并且对 groovy 认可的模块具有特殊含义。
-
module
- 要加载的模块的名称。直接转换为 Maven artifactId 或 Ivy 工件。 -
version
- 要使用的模块版本。字面版本或 Ivy 范围
表示 2.2.1 或任何更高版本)。
-
classifier
- 要使用的可选分类器(例如,jdk15)
下载的模块将按照 Ivy 的标准机制存储,缓存根为
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 代码依赖于此代码之前,让类加载器可以使用这些库。一些典型的调用可能如下所示
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:
参数传递的对象的 ClassLoader,及其父类加载器。 -
对
grab
发出调用的类的 ClassLoader
-
3.3.1. grab(HashMap) 参数
-
group:
- <String> - 模块来自哪个模块组。直接转换为 Maven 组 ID。任何匹配/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' - 要下载的模块的配置或范围。默认的 conf 为 `default:
,它映射到 mavenruntime
和master
范围。 -
force:
- <boolean>,默认为 true - 用于指示在冲突情况下此版本必须使用,与 -
冲突管理器
-
changing:
- <boolean>,默认为 false - 指示项目是否可以在版本名称保持不变的情况下进行更改。 -
transitive:
- <boolean>,默认为 true - 指示是否解析此模块具有的其他依赖项。
grab
有两个主要变体,一个带有一个 Map,另一个带有一个参数 Map 和多个依赖项映射。对单个映射 grab 的调用与使用在两次中传入相同的映射调用 grab 相同,因此 grab 参数和依赖项可以混在同一个映射中,且 grab 可以作为单一方法调用带有命名参数。
这些参数有同义词。提交多个同义词将产生运行时异常。
-
group:
,groupId:
,organisation:
,organization:
,org:
-
module:
,artifactId:
,artifact:
-
version:
,revision:
,rev:
-
conf:
,scope:
,configuration:
3.3.2. Arguments Map 参数
-
classLoader:
- <GroovyClassLoader> 或 <RootClassLoader> - 将已解析的 JAR 添加到的 ClassLoader -
refObject:
- <Object> - 对象类的最近父 ClassLoader 将像传入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 集合
// 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 集合
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 及其依赖项,并将它们缓存。我们在端口 8080 上创建了一个新的 Jetty Server,然后在上下文的根部显示 Groovy 的 TemplateServlet — Groovy 拥有其自己的强大模板引擎机制。我们启动服务器,并使其运行一段时间。每次有人访问 https://127.0.0.1:8080/somepage.gsp 时,都会向用户显示 somepage.gsp 模板 — 这些模板页面应位于与此服务器脚本相同的目录中。