Grape 相依性管理員

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 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 Shell 使用 Grape

從 groovysh 使用方法呼叫變體

groovy.grape.Grape.grab(group:'org.springframework', module:'spring', version:'2.5.6')

1.7. 代理設定

如果你在防火牆之後和/或需要透過代理伺服器使用 Groovy/Grape,你可以透過指令指定那些設定,例如透過 http.proxyHosthttp.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 可調整套件引擎Groovy 進階套件引擎)是 Groovy 中啟用 grab() 呼叫的基礎架構,它是一組利用 Ivy 的類別,讓 Groovy 能夠使用儲存庫驅動的模組系統。這讓開發人員能夠撰寫一個具有任意函式庫需求的指令碼,並只發布該指令碼。在從 Maven Central 等現有儲存庫執行指令碼時,Grape 會在執行階段根據需要下載並連結指定的函式庫和所有形成傳遞封閉的相依性。

Grape 遵循 Ivy 的模組版本識別慣例,並更名。

  • group - 模組來自哪個模組群組。直接轉換為 Maven groupId 或 Ivy 組織。任何符合 /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 地圖具有評估為 true 的 noExceptions 屬性,則不會擲回任何例外。

  • grab 要求指定 RootLoaderGroovyClassLoader,或在呼叫類別的 ClassLoader 鏈中。預設情況下,如果沒有這樣的 ClassLoader 可用,則會導致模組解析和擲回例外

    • 透過 classLoader: 參數傳遞的 ClassLoader 及其父類別載入器。

    • 傳遞為 referenceObject: 參數的物件的 ClassLoader 及其父類別載入器。

    • 發出呼叫至 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' - 要下載的模組的組態或範圍。預設 conf 為 `default:,對應到 maven runtimemaster 範圍。

  • force:- <boolean>,預設為 true - 用於表示在發生衝突時必須使用此修訂版本,與

  • 衝突管理員無關

  • changing: - <boolean>,預設為 false - 人工製品是否可以在不變更其版本指定的情況下變更。

  • transitive: - <boolean>,預設為 true - 是否解析此模組所具有的其他相依性。

grab 有兩個主要變體,一個使用單一 Map,另一個使用引數 Map 和多個相依性 map。呼叫單一 map grab 與呼叫 grab 並將相同的 map 傳入兩次相同,因此 grab 引數和相依性可以在同一個 map 中混合,且 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:

  • 驗證: - <布林>,預設為 false - 是否驗證 pom 或 ivy 檔案(true),或是否信任快取(false)。

  • 無例外: - <布林>,預設為 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.'**'.a.@href.grep(~/.*\.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 及其相依關係,並將其快取。我們在埠 8080 上建立一個新的 Jetty 伺服器,然後在內容的根目錄公開 Groovy 的 TemplateServlet — Groovy 附帶其自己的強大範本引擎機制。我們啟動伺服器並讓它執行一段時間。每次有人會按一下 https://127.0.0.1:8080/somepage.gsp,它會向使用者顯示 somepage.gsp 範本 — 這些範本頁面應與此伺服器腳本位於同一個目錄中。