1. 程式人生 > >《Groovy官方教程》Grape依賴管理器

《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給使用者,這些模板頁是放在一個相同的目錄下座位伺服器指令碼。