《Groovy官方教程》Grape依賴管理器
1 快速入門
1.1新增一個依賴
Grape是一個內嵌在Groovy裡的Jar包依賴管理器。Grape讓你可以快速新增maven倉庫依賴到你的classpath裡,使指令碼執行更加簡單。最簡單的一種用法是隻需要在你的腳本里新增一個註解:
@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate
@Grab也支援簡潔版:
@Grab('org.springframework:spring-orm:3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate
注意,這裡我們用到了import,這是推薦的做法。當然你也可以在mvnrepository.com搜尋到依賴包然後使用@Grab註解形式加到pom.xml實體裡。
1.2特定的倉庫
不是所有的依賴都在maven中心倉庫裡,你可以像下面這樣新增新的倉庫:
@GrabResolver(name='restlet', root='http://maven.restlet.org/') @Grab(group='org.restlet', module='org.restlet', version='1.1.6')
1.3Maven分類器
有些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')
(譯者注:原文標題是Excluding Transitive Dependencies,譯者翻譯為過渡依賴。譯者理解為那些非穩定版本的依賴,如果讀者有更好的理解或翻譯請留言,謝謝)
1.5JDBC驅動
你可能需要載入JDBC驅動,因此你需要將JDBC驅動依賴新增到系統類載入器中,示例如下:
@GrabConfig(systemClassLoader=true) @Grab(group='mysql', module='mysql-connector-java', version='5.1.6')
1.6從Groovy Shell中使用Grape
通過groovysh使用grape需要呼叫一個變數:
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將會列印下面的一些系統資訊:
- 開始解析依賴
- 開始下載工件(原文為artifact,工件的意思)
- 重新下載工件
- 現在工件的時間和大小
如果要更加詳細的日誌,可以通過提高日誌級別(預設為-1),示例如下:
-Divy.message.logger.level=4
2 詳細
Grape(The Groovy Adaptable Packaging Engine or Groovy Advanced Packaging Engine))是Groovy裡內嵌的一個基礎元件,通過grab()方法呼叫,一些類可以使用Ivy來打包成倉庫給Groovy用。這就支援開發者通過寫一段指令碼來實現一些核心庫的需求。裝載這段指令碼,Grape將會在執行時下載並連結所有依賴的庫。即便這些庫存在於Jcenter,Ibiblio和java.net
Grape遵從Ivy對模組版本標識和命名轉換。
- group – 模組屬於一個模組組。對應Maven的groupId或者一個Ivy組織。任何匹配/groovy[x][\..*]^/的組都是被保留的,對於Groovy是有特定意義的。
- module – 載入的模組名字,對應Mavne的artifactId或Ivy的artifact
- version – 模組使用的版本號,要麼是字串 ‘1.1-RC3’,要麼是Ivy的[2.2.1,)代表2.2.1或更高版本
- classifier – 可選的分類器,比如jdk15
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註解
如果需要在同一個節點使用一個註解多次可以使用@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
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是等價的,但是如果使用不同的類載入器可能需要重新執行
- 如果引數列表傳到grab呼叫有一個noExceptions引數,那麼將不會丟擲引數
- grab需要一個rootLoader或GroovyClassLoader,也可以是呼叫類的類載入器鏈。預設情況下列的情形將會失敗並且丟擲異常
- 通過classLoader傳遞的類載入器:引數和它的父類載入器
- 物件的載入器作為引用物件傳入,引數和它的父類載入器
- 類的類載入器呼叫grab
3.3.1 grab(HashMap)引數
- group: – – 模組所屬的模組組,對應Maven的groupId,任何匹配/groovy(|\..|x|x\..)/的組都是保留的,在Groovy模組中有特殊用途
- module:– 要載入的模組名,對應Maven的artifactId
- version:-也有可能是-使用的模組版本號,要麼是字串1.1-RC3要麼是Ivy Range [2.2.1,)表示2.2.1版本或更高版本
- classifier:–Maven解析的分類器
- conf:-,預設default’ – The configuration or scope of the module to download. The default conf is `default:對映到maven的runtime和master範圍
- force-預設是true,用於衝突時的修復,依賴於衝突管理器
- changing:-,預設是false,是否工件可以修改而不管版本的改變
- transitive:-,預設是true,是否解析其他依賴
grab有兩個基本變數,一個單個的map一個是帶一個map和多個依賴的map。呼叫單個map的grab和將一個相同的map傳入兩次呼叫是一樣的,因此grab引數和依賴可以混合在一個map裡,grab可以當成一個具有命名好引數的方法呼叫。
下面的引數每組都是相同的意思,如果傳入多餘一個將是一個執行時錯誤
- group:, groupId:, organisation:, organization:, org:
- module:, artifactId:, artifact:
- version:, revision:, rev:
- conf:, scope:, configuration:
3.3.2 引數列表
- classLoader: – 或-解析jar包的類載入器
- refObject:–最近的父類載入器,可以作為載入器引數被傳遞
- validate:-,預設是false,是否驗證poms或ivy檔案,或者是否直接信任快取資訊(預設false)
- noExceptions:-,預設是false,如果類載入器或參考查詢失敗,是否應該丟擲異常或吞併異常(預設情況)
3.4 命令列工具
Grape增加了一個命令列來執行grape,可以用來檢視和管理本地grape快取。
grape install <groupId> <artifactId> [<version>]
這條命令會安裝特定的groovy模組或maven工件,如果指定了版本,那麼將安裝特定的的版本,否則安裝最新的版本(類似我們傳遞 * 引數)
grape list
列出本地安裝的模組(如果是groovy模組,會顯示完整的maven名字)和版本
grape resolve (<groupId> <artifactId> <version>)+
返回安裝的模組或工件的檔案位置,並且會返回傳遞依賴模組的位置。你可以傳入可選引數-ant,-dos,-shell中來得到ant指令碼,windows批處理或unix shell指令碼格式檔案,-ivy將會得到類ivy格式的依賴
3.5 高階配置
3.5.1 倉庫目錄
如果你需要改變下載庫的grape目錄,可以使用grape.root系統屬性來改變預設值(預設值是~/.groovy/grape)
groovy -Dgrape.root=/repo/grape yourscript.groovy
3.5.2 自定義Ivy設定
你可以自定義ivy的是指,通過建立一個~/.groovy/grapeConfig.xml檔案,如果沒有這個檔案,Grape會使用預設配置,詳細參考這裡。需要查詢更多關於自定義設定的文件,可以參考Ivy文件。
3.6 更多示例
使用Apache Commons集合工具類
// create and use a primitive array list import org.apache.commons.collections.primitives.ArrayIntList @Grab(group='commons-primitives', module='commons-primitives', version='1.0') 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模組
@Grapes([ @Grab(group='org.eclipse.jetty.aggregate', module='jetty-server', version='8.1.7.v20120910'), @Grab(group='org.eclipse.jetty.aggregate', module='jetty-servlet', version='8.1.7.v20120910'), @Grab(group='javax.servlet', module='javax.servlet-api', version='3.0.1')]) import org.eclipse.jetty.server.Server import org.eclipse.jetty.servlet.* import groovy.servlet.* 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服務,並且將Groovy的TemplateServlet最為根服務。Groovy有自己強大的模板引擎機制。我們啟動這個服務並且執行一段時間。每次有人訪問http://localhost:8080/somepage.gsp的時候,它會顯示somepage.gsp給使用者,這些模板頁是放在一個相同的目錄下座位伺服器指令碼。