【認識 Gradle】(7)Java 專案相依管理

- gradle-series

編:此文撰寫於 2014 年初,當時的 SoneTypa Nexus 已經改名為 Nexus Repoistory 並分為 OSS 版本與 Pro 版本

在 Maven 帶給開發者許多好的體驗,相依性管理是其中對一般開發者較具有吸引力的因素。Gradle 納入套件管理的功能,並且能使用 Maven Repository 作為套件下載來源。在 Gradle 管理套相的相依性,可以寫成這樣:

apply plugin: 'java' 

/* 設定 maven repository server $ */
repositories {
    mavenCentral()
}

/* 宣告專案的相依函式庫 $ */
dependencies {
    compile group: 'commons-logging', name: 'commons-logging', version: '1.1.1'
    compile group: 'log4j', name: 'log4j', version: '1.2.16'
}

使用 repositories 設定決定套件的來源,再使用 dependencies 宣告相依關係。這樣寫幾乎就是快速上手課程裡會學到的內容,不過文章才剛開始,我們不會那麼早結束這個『話題』。

記得看過一則討論是使用 Maven 管理相依性問題是否實用嗎?讓專案直接與使用到的函式庫(JARs)包含開發環境的相依套件設定,通通送進版本控制系統。當有人需要重新建起開發環境時,只要由版本控制系統取回匯入 IDE 後就能使用。我必需承認這樣確實很方便,不過有幾個問題需要思考。

首先,版本控制系統較擅長處理純文字的檔案,對於函式庫這類非純文字檔的存儲效率不太好,無法以較省空間的方式儲存。隨著函式庫更換版本的次數越多,版本控制系統檔案庫虛胖得越快。若你的團隊可能有多個地理位置,只有靠近版本控制系統的人可以使用愉快。專案開發使用的函式庫檔案越大,由版本控制系統取出的時間越久。這雖然是理所當然的事,但曾待過這樣的團隊,版本控制系統的伺服務在遠方,只有與它同辦公室的同事覺得取出專案很快。若你們團隊都離版本控制系統伺服務很近,那麼這個『等待』的問題應該還算在能接受的範圍。

另一個問題是,在同一組開發團隊裡,使用的解決方案可能會很相近,使用的函式庫相互重複的情況相當明顯。相同的 Logging Tool、相同的 IoC Framework、相同的 Web Framework、相同的 ORM Framework 或公司累積的內部函式庫。可以試著算算公司內有多少相近的專案,並想一下 N 個專案,就有 N 組重複的函式庫在版本控制系統內,最好提醒管理版本控制系統伺服器端的同事,硬碟空間不能太小氣。若換成是使用『套件相依管理』的功能,那麼它的變動只是一個文字檔內的幾行字,對於版本控制系統的負擔很小。

除了上述的缺點之外,我們得回到相依管理的本質。它並不是在管理一個一個的 JAR 檔案是否要存在這個專案。它是在管理『一組』JARs 檔,它們一起出現是具有意義的,而那個意義才是『相依性』管理的核心。就像在專案中使用 Hibernate 或 Spring Framework 不會只有它們本身提供的 JARs 就足夠,還有相依的第三方函式庫。若單純以獨立的 JAR 檔的角度來管理專案需要用到的函式庫,那隨著專案的發展有些功能已不再使用,年代久遠之後,也沒有人會記得哪一個 JAR 檔是為了什麼目的而加進來的,特別是這個 JAR 檔為了滿足函式庫自身相依性的情況。

相依關係

我們使用 gradle tasks 查詢可用的 task,當你加入 dependencies 設定後,會多出相關的 task:

Help tasks
----------
dependencies - Displays all dependencies declared in root project 'gradleLab'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradleLab'.
help - Displays a help message
projects - Displays the sub-projects of root project 'gradleLab'.
properties - Displays the properties of root project 'gradleLab'.
tasks - Displays the tasks runnable from root project 'gradleLab'.

使用 dependencies task 可以查詢專案目前的相依關係,其中 compile 設定 (專用術語是 dependency configurations) ,就是在 build.gralde 內寫的設定,其他部分是設定 compile 相依套件時自動設定的:

qty:gradleLab qrtt1$ gradle dep
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16

default - Configuration for default artifacts.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16

runtime - Runtime classpath for source set 'main'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16

testCompile - Compile classpath for source set 'test'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16

testRuntime - Runtime classpath for source set 'test'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16

BUILD SUCCESSFUL

目前專案看單純僅有二個相依套件設定,我們試引用其它稍為巨大一點的函式庫試試,在 dependencies 設定內只留下 AWS SDK for Java:

com.amazonaws:aws-java-sdk:1.+
compile - Compile classpath for source set 'main'.
\--- com.amazonaws:aws-java-sdk:1.+ -> 1.6.11
     +--- commons-logging:commons-logging:1.1.1
     +--- org.apache.httpcomponents:httpclient:4.2
     |    +--- org.apache.httpcomponents:httpcore:4.2
     |    +--- commons-logging:commons-logging:1.1.1
     |    \--- commons-codec:commons-codec:1.6
     +--- commons-codec:commons-codec:1.3 -> 1.6
     +--- com.fasterxml.jackson.core:jackson-core:2.1.1
     +--- com.fasterxml.jackson.core:jackson-databind:2.1.1
     |    +--- com.fasterxml.jackson.core:jackson-annotations:2.1.1
     |    \--- com.fasterxml.jackson.core:jackson-core:2.1.1
     +--- com.fasterxml.jackson.core:jackson-annotations:2.1.1
     \--- joda-time:joda-time:[2.2,) -> 2.3

由相依關係可以看出,aws-java-sdk 宣告時指定需要版號 1. 開頭的,它在 1. 相關的版本找目前最新的 1.6.11,它本身的相依於:

  1. commons-logging:commons-logging:1.1.1
  2. org.apache.httpcomponents:httpclient:4.2
  3. commons-codec:commons-codec:1.3
  4. com.fasterxml.jackson.core:jackson-core:2.1.1
  5. com.fasterxml.jackson.core:jackson-databind:2.1.1
  6. com.fasterxml.jackson.core:jackson-annotations:2.1.1
  7. joda-time:joda-time:[2.2,)

在 org.apache.httpcomponents:httpclient:4.2 套件它又有自己相依的套件:

  1. org.apache.httpcomponents:httpcore:4.2
  2. commons-logging:commons-logging:1.1.1
  3. commons-codec:commons-codec:1.6

我們能觀察到 commons-codec:commons-codec:1.6 與 aws-java-sdk 指定的 1.3 版不同,相依性管理的機制會選用較新的那一組,這也是為什麼它最後被標示為:

commons-codec:commons-codec:1.3 -> 1.6

因為原先指定的版本是 1.3,但其它相依套件指定了更新的版本,於是最終使用 1.6 版。

另一組是只留下 Windows Azure SDK for Java:

compile 'com.microsoft.windowsazure:microsoft-windowsazure-api:0.+'
compile - Compile classpath for source set 'main'.
\--- com.microsoft.windowsazure:microsoft-windowsazure-api:0.+ -> 0.4.6
     +--- com.sun.jersey:jersey-client:1.13
     +--- javax.inject:javax.inject:1
     +--- com.sun.jersey:jersey-json:1.13
     |    +--- org.codehaus.jettison:jettison:1.1
     |    |    \--- stax:stax-api:1.0.1
     |    +--- com.sun.xml.bind:jaxb-impl:2.2.3-1
     |    |    \--- javax.xml.bind:jaxb-api:2.2.2
     |    |         +--- javax.xml.stream:stax-api:1.0-2
     |    |         \--- javax.activation:activation:1.1
     |    +--- org.codehaus.jackson:jackson-core-asl:1.9.2
     |    +--- org.codehaus.jackson:jackson-mapper-asl:1.9.2
     |    |    \--- org.codehaus.jackson:jackson-core-asl:1.9.2
     |    +--- org.codehaus.jackson:jackson-jaxrs:1.9.2
     |    |    +--- org.codehaus.jackson:jackson-core-asl:1.9.2
     |    |    \--- org.codehaus.jackson:jackson-mapper-asl:1.9.2 (*)
     |    \--- org.codehaus.jackson:jackson-xc:1.9.2
     |         +--- org.codehaus.jackson:jackson-core-asl:1.9.2
     |         \--- org.codehaus.jackson:jackson-mapper-asl:1.9.2 (*)
     +--- commons-logging:commons-logging:1.1.1
     +--- javax.mail:mail:1.4.5
     |    \--- javax.activation:activation:1.1
     \--- org.apache.commons:commons-lang3:3.1

Windows Azure 相依關係就更多層,樹狀結構更深。在專案使用越多的第三方套件,那麼要明確瞭了哪一個 JAR 檔的來歷與功用,為何將它加入專案內就越加困難。並且使用相依性管理功能能指定一個可接受的版本號範圍,對 Windows Azure 指定的是 0.+,就是要使用主版號為 0 的套件中最新的版本,這功能能輔助專案的相依套件隨時更新到足夠新的狀態。

更新到足夠新的狀態有正反兩種意義:

  1. bugfix 與 patch 修補的版本會更得上最近的發佈版本
  2. 潛在的 bug 也隨著最新的版本引入

在希望專案自動緊追著最新發佈版本的情況採用自動版本號,不希望它自動更新的情況下使用固定的版本號。以 AWS SDK for Java 為例,在公司的專案最初設定自動版號,但發現一旦 AWS 有新服務發表,或是一些我們不會用到的功能修好 bug 就會有新的版本。由於它變動得太過頻繁,重新下載相依檔案就會花一點時間,但我們又用不到它更新的部分,於是將它設為固定版號的。

公司內部發佈的套件相依設定皆為自動版號,因為不會無來由地發佈新版,肯定是修了 Bug 或要加新功能讓需要的專案使用。它仍然有改成固定版號的時機,例如:新版的需要測試後才能發佈,那麼現行的產品就應相依於特定版號,直到新版的確定要發佈且不會再有重大修改。另一個原因是規格變更前後版不相容,不過這情況就不建議改相依性的版號,而是新的版本要提昇主版號,表示為不相容。

相依關係設定

理解相依管理的優點後,在開發中如何使用它呢?對於沒有 Maven 或 Ant Ivy 使用經驗的讀者應該會有第一個疑惑, 在 dependencies 設定:

compile group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.+'

或寫成:

compile 'com.amazonaws:aws-java-sdk:1.+'

這套件資訊是怎麼來的呢?有幾個途經:

  1. 至該套件的官方網站查詢,通常手冊開發者的訊息會包含。
  2. 若是 open source 專案,由它的原始碼找答案它很可能是 Maven Project 或 Gradle Project。
  3. 使用套件查詢網站查詢,但仍建議比對官網目前發部的版本。

使用套件查詢網站時,keyword 通常是以專案名稱,或該專案使用的 package name 為主,例如查詢 aws:

找到目標後進去它的內頁:

選擇需要的版本後,依專案工具選擇適當的設定值。請點選 Gradle 頁籤:

在 Gradle 頁籤內的資訊能直接填寫至 dependencies 內,或將它改成動態版本的宣告。

套件伺服器

當專案開始利用 Gradle 管理套件的便利設施後,隨著管理與使用的需求,架設套件伺服器是隨之而來的必要工作。原先我們針對套件伺服器的設定只有簡單地採用 Maven 公開伺服器,單純作為 Open Source 函式庫的使用者來說,它是方便的:

repositories {
    mavenCentral()
}

不過對於要開發私有程式的商業單位,多數的情況不會將自己公司的函式庫發佈至 Maven 公開伺服器。取而代之的是架設自有的套件伺服器,將公司內部的函式庫發佈在私有的伺服器上,後續專案只要設定好 repositories 就能使用,例如:

repositories {
    maven {
        credentials {
            username 'username-for-your-server'
            password 'password-for-your-server'
        }
        url "http://url-to-your-repo-server"
    }
}

有了私有伺服器,通常就不會額外宣告 mavenCentral(),因為它同時作為『代理伺服器』替開發者向 Maven 公開伺服器下載需要的檔案,這樣亦可減少非必要的網路傳輸。

我們能在網路上找到許多套件伺服器,以本系列常引用的 Nexus 來說,它就是其中一套。它提供 Open Source 版與商業版,如果沒有特殊的需求用 Open Source 版已經堪用。它是以 Java Solution 寫成的,安裝的方法可參閱其官方文件 Installing Nexus。它有二種安裝方式:一種是安裝 WAR 檔版本,就使用你原有的 Java Web Container 即可,或是用獨立執行的版本(它是使用 jetty)。

qty:nexus-2.7.1-01-bundle qrtt1$ ls -alh
total 0
drwx------@  4 qrtt1  staff   136B  1 18 12:37 .
drwx------+ 32 qrtt1  staff   1.1K  1 18 12:30 ..
drwxr-xr-x@ 10 qrtt1  staff   340B  1 10 11:05 nexus-2.7.1-01
drwxr-xr-x@  5 qrtt1  staff   170B  1 18 12:30 sonatype-work

以獨立執行的版本為例,下載完解壓縮後會有二個目錄。nexus 開頭的目錄為程式的目錄,而 sonatype-work 是預設存放下載資料的目錄(可以透過設定變更,維護的工作主要也是備份這個目錄的資料)。對大多數管理者來說有額外設定的需求,例如:改管理者帳號、改 port 號。

針對 Web Container 部分的設定,可由程式目錄下的 conf/nexus.properties 修改:

qty:nexus-2.7.1-01-bundle qrtt1$ cat nexus-2.7.1-01/conf/nexus.properties 
# Sonatype Nexus
# ==============
# This is the most basic configuration of Nexus.

# Jetty section
application-port=8081
application-host=0.0.0.0
nexus-webapp=${bundleBasedir}/nexus
nexus-webapp-context-path=/nexus

# Nexus section
nexus-work=${bundleBasedir}/../sonatype-work/nexus
runtime=${bundleBasedir}/nexus/WEB-INF

其他部分需啟動程式後,由 Web Console 修改。像是他預設的管理者帳號:admin/admin123,就是需要修改的。這部分就不在本文的重點,依讀者依使用手冊 Post-Install Checklist 的建議修改。額外需要提醒讀者的是:

  1. 將 anonymous 使用者停用(或刪除),預設是任何人可以在不登入 nexus 的情況下讀取 library。
  2. 變更 admin 使用者密碼,或停用另建管理者帳號。
  3. 替不同單位建立不同的 deploy 帳號。

登入 nexus 後,在 repositories 管理頁可以看到許多事先建立完成的 repository:

可以觀察到每一個 repository 都有一個 type:

  1. proxy:那些常用的、公開的 Maven Repository 已預先建立成 proxy 的 type,當你以這台自建的 nexus server 作為 repository 時,它就會作為代理人替你去向公開 repository 抓取相依套件,存放在 sonatype-work 目錄內。第二次再抓取出,若沒有更新的版本就直接回傳先前抓取的版本。
  2. hosted:type 為 hosted 代表是使用者自行發佈的內容,我們常用的就是 Releases 與 Snapshots 這二組 repository,也有人更細分出 3rd party,或是全都用 Releases 發佈。
  3. group:列在第 1 項的 Public Repositories 標為 group type 的是一種特殊的 repository,可以想像成是 Server Side 的反向代理,因為它本身沒有直接 hosted 或 proxy 的功能,而是將一些既有的 repository 聚合起來成為一個進入點。設定 Maven Repository 引用位置時可以設定這一組位置即可,不需要設定個別的 Releases 或 Snapshots Repository。

點開 group repository 的設定,可以看出它走訪不同 repository 的順序:

使用自有套件

完成自有 repository 架設後,將專案設定稍作修改即可將它發佈並在其他專案引用。在學習發佈之前,我們能先修改專案使用自己的 repository server,將原先的 mavenCentral() 換成自建的 repository:

注意!這是個簡單的示範,正式啟用前應建好新的帳號,而不是使用 admin 帳號,並確定好 server 固定 ip,而非 localhost 位置。

apply plugin: 'java'
apply plugin: 'maven'

repositories {
    maven {
        credentials {
            username 'admin'
            password 'admin123'
        }
        url "http://127.0.0.1:8081/nexus/content/groups/public/"
    }
}

dependencies {
    compile group: 'commons-logging', name: 'commons-logging', version: '1.1.1'
    compile group: 'log4j', name: 'log4j', version: '1.2.16'
}

完成修改後,專案就不再直接抓取 Maven 公開的 repository,而是透過 nexus 作為 proxy 進行核對版本。若要對此專案進行發佈,需再加上 uploadArchives 設定,並指定要發佈的 repository 位置:

/* build.gradle */
apply plugin: 'java'
apply plugin: 'maven'

ext {
    maven_group_id = 'codedata'
    maven_artifact_id = 'gradle.happy.tour'
    maven_version = '0.1.3'
}

dependencies {
    compile group: 'commons-logging', name: 'commons-logging', version: '1.1.1'
    compile group: 'log4j', name: 'log4j', version: '1.2.16'
}
apply from: 'mvn.gradle'
/* mvn.gradle */
apply plugin: 'maven'

ext {
    maven_repo_id = 'admin'
    maven_repo_pwd = 'admin123'
}

repositories {
    maven {
        credentials {
            username maven_repo_id
            password maven_repo_pwd
        }
        url "http://127.0.0.1:8081/nexus/content/groups/public/"
    }
}

def enableUploadArchives = ext.has('maven_group_id') && ext.has('maven_artifact_id') && ext.has('maven_version') 

if (enableUploadArchives) {
    uploadArchives {
        repositories {
            mavenDeployer {
                pom.groupId = maven_group_id
                pom.artifactId = maven_artifact_id
                pom.version = maven_version

                def suffix = pom.version.contains("SNAPSHOT") ? "snapshots" : "releases"
                repository(url: "http://127.0.0.1:8081/nexus/content/repositories/${suffix}/") {
                    authentication(userName: maven_repo_id, password: maven_repo_pwd)
                }
            }
        }
    }
}

讀者可以注意到,我們做了一些改變:

  1. 將 Maven 相關設定獨立到其它的檔案,在還沒有學會寫 plugin 前,單純透過 apply 來引用 script 是個方便的替代方案
  2. 在 uploadArchives 有個簡單的邏輯判斷。當版本號含 SNAPSHOT 字串時,就將 repository 設成 snapshot 的那一組,例如 0.1.2-SNAPSHOT。反之,則發佈至 releases 的那一組。
  3. uploadArchives 只在有滿足 mavengroupid、mavenartifactid、maven_version 才設定 repository 上傳。

一旦設定完成,我們能透過 gradle uploadArchives 指令上傳,並能看到顯示上傳到 Server 的訊息。下面範例是發佈 1.0 版本的訊息:

qty:HelloGradle qrtt1$ gradle uArc
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:uploadArchives
Uploading: codedata/gradle.happy.tour/1.0/gradle.happy.tour-1.0.jar to repository remote at http://127.0.0.1:8081/nexus/content/repositories/releases/
Transferring 0K from remote
Uploaded 0K

BUILD SUCCESSFUL

Total time: 6.724 secs

接著,我們能在另一個專案引用它,我們使用動態版本號:

qty:UsingDeps qrtt1$ cat build.gradle 
apply plugin: 'java'
apply plugin: 'maven'

dependencies {
    compile group: 'codedata', name: 'gradle.happy.tour', version: '1.+'
}

apply from: 'mvn.gradle'

並能由 dependency task 查出目前的相依關係:

qty:UsingDeps qrtt1$ gradle dep
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
\--- codedata:gradle.happy.tour:1.+ -> 1.0
     +--- commons-logging:commons-logging:1.1.1
     \--- log4j:log4j:1.2.16

..................................................

接著讀者可做個簡單的練習:

  1. 在 HelloGradle 專案,發佈 1.1 版
  2. 觀察 UsingDeps 的相依關係是哪一版

結果是 UsingDeps 仍停留在 1.0 版,因為 gradle 並不會主動檢查 Server 端是否有新版本,可以下參數強制它檢查:

qty:UsingDeps qrtt1$ gradle dep --refresh-dependencies
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
\--- codedata:gradle.happy.tour:1.+ -> 1.1
     +--- commons-logging:commons-logging:1.1.1
     \--- log4j:log4j:1.2.16
..................................................

這樣透過 gradle 發佈與引用套件至自建 repository 相當方便我們管理自制的函式庫,需要注意的是相依的版本不會主動更新,需透過 --refresh-dependencies 強制它更新至最新版本。

使用 Maven 上傳函式庫

教完 Gradle 的上傳繼續談 Maven,這並不是讀者眼花,是因為有太多情況會遇到 Maven Project,特別是使用 Open Source 專案的情況,並非所有的專案都轉向 Gradle。所以,學習如何處理 Mavan 專案也是份重要的知識。儘管可以在網路上找到各種用 Gradle 上傳單一個 JAR 檔,或整組 Maven Project(或先轉成 Gradle Project)。做法上不太一致,那不如退回到對它支援對好的工具:用 Maven 上傳至 Maven Reposiotry。得利用 Maven 上傳,像是:

  1. 不管什麼原因,得上傳獨立的 JAR 檔。像是合作的伙伴沒有釋出原始碼,只給了編譯好的 JAR 檔,或是古早年代前人留下來的檔案。
  2. 修改其他 Open Source 的 Maven 專案:這通常是『等不及』或『等不到』發佈需要的函式庫時需要做的。等不及就是已經有 bug fixed 在版本控制系統內了,只是該專案 release 時間遙遙無期,至少它遠大於你本身的 deadline。而等不到的情況是,該專案沒有活動機象了,好家在還能 build 出 JAR 檔,只好拿著 source code 自己來吧。

用 Maven 上傳函式庫的方法就是使用 maven deploy plugin,不過我們不討論原本就是 Maven 專案並且已設好上傳位置的狀態。單純談使用 deploy plugin 內的 deploy-file 功能,用它來處理需要單檔上傳的情況。

設定 Maven Repository 證認資料

Maven 專案的 Repository 資訊是設定在專案內,但認證資料是設定在安裝目錄內的 $MAVEN_HOME/conf/settings.xml,它的結構如下:

<settings>
    <servers>
        <server>
          <id>my_maven_repo</id>
          <username>admin</username>
          <password>admin123</password>
        </server>   
    </servers>
</settings>

使用 deploy plugin 時,需指定 server id my_maven_repo,它會找出對應的帳號與密碼。假設現在有個 legacy.jar 檔,我們要將它上傳至自建的 repository:

mvn deploy:deploy-file \
-Durl=http://127.0.0.1:8081/nexus/content/repositories/releases \
-DrepositoryId=my_maven_repo \
-Dfile=legacy.jar \
-Dpackaging=jar \
-DgroupId=example.com \
-DartifactId=foo.bar \
-Dversion=1.0

參數說明如下:

  1. 使用 deploy-file,在沒有事先撰寫 pom.xml 的情況下,能透過 url 參數指定 repository 位置。
  2. 參數 repositoryId 就是在 settings.xml 內設定的 server id,會用它來查出對應的認證資訊
  3. 參數 file 為欲上傳檔案的路徑
  4. packaging 為檔案格式,上傳 JAR 檔時填 jar 即可
  5. groupId、artifactId、version 都是 Maven 專案必填的項目

上傳過程顯示訊息如下:

qty:legacyJar qrtt1$ mvn deploy:deploy-file \
> -Durl=http://127.0.0.1:8081/nexus/content/repositories/releases \
> -DrepositoryId=my_maven_repo \
> -Dfile=legacy.jar \
> -Dpackaging=jar \
> -DgroupId=example.com \
> -DartifactId=foo.bar \
> -Dversion=1.0
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-deploy-plugin:2.5:deploy-file (default-cli) @ standalone-pom ---
Uploading: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/1.0/foo.bar-1.0.jar
Uploaded: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/1.0/foo.bar-1.0.jar (108 KB at 718.6 KB/sec)
Uploading: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/1.0/foo.bar-1.0.pom
Uploaded: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/1.0/foo.bar-1.0.pom (389 B at 7.6 KB/sec)
Downloading: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/maven-metadata.xml
Downloaded: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/maven-metadata.xml (294 B at 9.9 KB/sec)
Uploading: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/maven-metadata.xml
Uploaded: http://127.0.0.1:8081/nexus/content/repositories/releases/example/com/foo.bar/maven-metadata.xml (323 B at 6.2 KB/sec)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.971s
[INFO] Finished at: Sat Jan 25 22:20:05 CST 2014
[INFO] Final Memory: 3M/81M
[INFO] ------------------------------------------------------------------------

完工後即可在 gradle 專案內使用,例如:

dependencies {
    compile group: 'codedata', name: 'gradle.happy.tour', version: '1.+'
    compile group: 'example.com', name: 'foo.bar', version: '1.+'
}

另一種上傳形式是指定 pom.xml,通常會用在本身是 Maven 專案有提供 pom.xml,且我們需要將它上傳至自建 repository 的情況:

mvn deploy:deploy-file \
-Durl=http://127.0.0.1:8081/nexus/content/repositories/releases \
-DrepositoryId=my_maven_repo \
-Dfile=legacy.jar \
-DpomFile=your-pom.xml

能由 pom.xml 內提供的資訊就不需要寫出來,另外好處是它不再只是單一檔案上傳,還能在 pom.xml 內描述它的相依關係。

內容回顧

這次談到 Java 專案的相依性管理,理解相依管理的要點與重要性。學習使用 Gradle 將專案發佈成函式庫,並以 Maven 工具補足額外的上傳需求:

  1. 設定 reposiotry 與 uploadArchives
  2. 以 –refresh-dependencies 參數強制更新
  3. 使用 nexus oss 架設自有的 Maven Repository
  4. 使用 Maven 上傳非 Gradle 專案的函式庫

我們講述了如何引用、發佈、更新函式庫,並了解特定情況的上傳方式。期待讀者能在相依管理的機制下,獲得更舒適的開發體驗。

編:上傳至 Maven Repository 的用法,在新版的 Gradle 有不同的寫法,請讀者參考 Maven Publish Plugin