建立 Java 和 JVM 專案
Gradle 使用慣例優先於組態的方式來建置基於 JVM 的專案,這種方式借用了許多 Apache Maven 的慣例。特別是,它使用相同的來源檔案和資源預設目錄結構,並與相容 Maven 的儲存庫搭配使用。
我們將在本章詳細探討 Java 專案,但大多數主題也適用於其他受支援的 JVM 語言,例如 Kotlin、Groovy 和 Scala。如果您在使用 Gradle 建置基於 JVM 的專案方面經驗不足,請參閱 Java 範例,其中提供逐步說明,說明如何建置各種類型的基本 Java 專案。
本節中的範例使用 Java 函式庫外掛程式。然而,所描述的功能由所有 JVM 外掛程式共用。不同外掛程式的具體資訊可在其專屬文件取得。 |
簡介
Java 專案最簡單的建置指令碼套用 Java 函式庫外掛程式,並可選擇設定專案版本和選取要使用的 Java 工具鏈
plugins {
`java-library`
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
version = "1.2.1"
plugins {
id 'java-library'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
version = '1.2.1'
套用 Java 函式庫外掛程式後,您會取得許多功能
-
compileJava
任務會編譯 src/main/java 底下的所有 Java 原始檔 -
compileTestJava
任務適用於 src/test/java 底下的原始檔 -
test
任務會執行 src/test/java 中的測試 -
jar
任務會將main
編譯類別和 src/main/resources 中的資源封裝到名為 <project>-<version>.jar 的單一 JAR 中 -
javadoc
任務會為main
類別產生 Javadoc
這不足以建置任何非瑣碎的 Java 專案,至少您可能會有一些檔案相依性。但這表示您的建置指令碼只需要專屬於 您的 專案的資訊。
雖然範例中的屬性為選用,但我們建議您在專案中指定這些屬性。設定工具鏈可防止專案使用不同 Java 版本建置時出現問題。版本字串對於追蹤專案進度非常重要。預設情況下,專案版本也會用在封存檔名稱中。 |
Java 函式庫外掛程式也會將上述任務整合到標準 基本外掛程式生命週期任務
-
jar
附加到assemble
-
test
附加到check
本章節的其餘部分說明自訂建置以符合您需求的不同途徑。稍後您也會看到如何調整建置以符合函式庫、應用程式、網頁應用程式和企業應用程式的需求。
透過來源集宣告您的來源檔案
Gradle 的 Java 支援最先為建置基於來源的專案引入一個新概念:來源集。主要概念是來源檔案和資源通常會依類型進行邏輯分組,例如應用程式程式碼、單元測試和整合測試。每個邏輯群組通常都有自己的檔案相依性、類別路徑等等。重要的是,組成來源集的檔案不必位於同一個目錄中!
來源集是一個強大的概念,它將編譯的幾個面向串連在一起
-
原始檔案及其所在位置
-
編譯類別路徑,包括任何必要的相依性(透過 Gradle 組態)
-
已編譯類別檔案放置的位置
您可以在此圖表中看到這些如何彼此相關

陰影方塊表示來源組本身的屬性。除此之外,Java 函式庫外掛程式會自動為您或外掛程式定義的每個來源組建立一個編譯工作,命名為 compileSourceSetJava
,以及幾個 相依性組態。
main
來源組大多數語言外掛程式(包括 Java)會自動建立一個名為 main
的來源組,用於專案的生產程式碼。此來源組的特殊之處在於其名稱未包含在組態和工作的名稱中,因此您只有一個 compileJava
工作和 compileOnly
及 implementation
組態,而不是 compileMainJava
、mainCompileOnly
和 mainImplementation
。
Java 專案通常包含除了原始檔案之外的其他資源,例如需要處理的屬性檔案(例如,取代檔案中的代幣)和封裝在最終 JAR 中。Java 函式庫外掛程式會透過為每個已定義的來源組自動建立一個專用工作來處理這個問題,稱為 processSourceSetResources
(或 processResources
,針對 main
來源組)。下圖顯示來源組如何與此工作配合

與之前一樣,陰影方塊表示來源組的屬性,在本例中包括資源檔案的位置以及複製到的位置。
除了 main
來源組之外,Java 函式庫外掛程式會定義一個 test
來源組,代表專案的測試。此來源組由 test
工作使用,該工作會執行測試。您可以在 Java 測試 章節中進一步了解此工作和相關主題。
專案通常將此來源組用於單元測試,但您也可以將其用於整合、驗收和其他類型的測試(如果您願意)。另一種方法是為每個其他測試類型 定義一個新的來源組,通常出於以下一個或兩個原因而執行此操作
-
您希望將測試彼此分開,以保持美觀和易於管理
-
不同的測試類型需要不同的編譯或執行時期類別路徑,或設定上的其他差異
您可以在 Java 測試章節中看到此方法的一個範例,其中會向您展示 如何在專案中設定整合測試。
您將在
來源組態設定
建立來源組態時,也會建立如上所述的許多設定。建置邏輯不應嘗試在來源組態建立這些設定之前建立或存取這些設定。
建立來源組態時,如果這些自動建立的設定之一已存在,Gradle 會發出不建議使用的警告。如果現有設定的角色與來源組態所指派的角色不同,其角色會變異為正確的值,並會發出另一個不建議使用的警告。
以下建置示範了這種不必要的行為。
configurations {
val myCodeCompileClasspath: Configuration by creating
}
sourceSets {
val myCode: SourceSet by creating
}
configurations {
myCodeCompileClasspath
}
sourceSets {
myCode
}
在這種情況下,會發出以下不建議使用的警告
When creating configurations during sourceSet custom setup, Gradle found that configuration customCompileClasspath already exists with permitted usage(s):
Consumable - this configuration can be selected by another project as a dependency
Resolvable - this configuration can be resolved by this project to a set of files
Declarable - this configuration can have dependencies added to it
Yet Gradle expected to create it with the usage(s):
Resolvable - this configuration can be resolved by this project to a set of files
遵循以下兩個簡單的最佳實務,可以避免這個問題
-
不要建立名稱會被來源組態使用的設定,例如以
Api
、Implementation
、ApiElements
、CompileOnly
、CompileOnlyApi
、RuntimeOnly
、RuntimeClasspath
或RuntimeElements
結尾的名稱。(此清單並非詳盡無遺。) -
在任何自訂設定之前建立任何自訂來源組態。
請記住,任何時候您在 configurations
容器中參照設定時,無論是否提供初始化動作,Gradle 都會建立設定。有時使用 Groovy DSL 時,這個建立並不明顯,如下面的範例所示,其中 myCustomConfiguration
在呼叫 extendsFrom
之前建立。
configurations {
myCustomConfiguration.extendsFrom(implementation)
}
如需更多資訊,請參閱 不要預期設定建立。
管理您的相依性
絕大多數的 Java 專案都仰賴函式庫,因此管理專案的相依性是建置 Java 專案的重要部分。相依性管理是一個大主題,因此我們將重點放在 Java 專案的基礎知識上。如果您想深入了解,請查看 相依性管理簡介。
指定 Java 專案的相依性只需要三項資訊
-
您需要的相依性,例如名稱和版本
-
需要它的原因,例如編譯或執行
-
在哪裡尋找它
前兩項指定在 dependencies {}
區塊中,第三項指定在 repositories {}
區塊中。例如,要告訴 Gradle 您的專案需要 Hibernate Core 的 3.6.7 版本來編譯和執行您的製作程式碼,而且您想要從 Maven Central 儲存庫下載函式庫,您可以使用以下片段
repositories {
mavenCentral()
}
dependencies {
implementation("org.hibernate:hibernate-core:3.6.7.Final")
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.hibernate:hibernate-core:3.6.7.Final'
}
Gradle 對這三個元素的術語如下
-
儲存庫(例如:
mavenCentral()
)—尋找您宣告為相依項的模組所在位置 -
組態(例如:
implementation
)—相依項的命名集合,針對特定目標(例如編譯或執行模組)分組在一起—Maven 範圍的更彈性形式 -
模組座標(例如:
org.hibernate:hibernate-core-3.6.7.Final
)—相依項的 ID,通常為「<group>:<module>:<version>」格式(或在 Maven 術語中為「<groupId>:<artifactId>:<version>」)
您可以在這裡找到相依項管理術語的更全面詞彙表。
就組態而言,主要的組態有
-
compileOnly
—對於編譯您的生產程式碼但不要成為執行時期類別路徑一部份的相依項 -
implementation
(取代compile
)—用於編譯和執行時期 -
runtimeOnly
(取代runtime
)—僅用於執行時期,不適用於編譯 -
testCompileOnly
—與compileOnly
相同,但適用於測試 -
testImplementation
—implementation
的測試等效項 -
testRuntimeOnly
—runtimeOnly
的測試等效項
您可以在外掛參考章節中進一步了解這些組態以及它們之間的關聯性。
請注意,Java 函式庫外掛提供兩個額外的組態—api
和compileOnlyApi
—適用於編譯模組和任何依賴於該模組的模組所需的相依項。
compile
組態?Java 函式庫外掛過去使用compile
組態,適用於編譯和執行專案的生產程式碼所需的相依項。它現在已不建議使用,而且在使用時會發出警告,因為它無法區分影響 Java 函式庫專案的公開 API 的相依項和不會影響的相依項。您可以在建立 Java 函式庫中進一步了解此區分的 важно性。
我們在此僅介紹了皮毛,因此建議您在熟悉使用 Gradle 建立 Java 專案的基本知識後,閱讀專門的相依項管理章節。需要進一步閱讀的一些常見情況包括
-
使用來自本機檔案系統目錄的相依項
-
宣告兄弟專案作為相依項
-
透過複合建置(比發佈到和使用Maven Local更好的替代方案)測試您對第三方相依項的修正
您會發現 Gradle 有一個豐富的 API 可用於處理相依性,需要花時間才能精通,但對於常見情境來說,使用起來很簡單。
編譯您的程式碼
如果您遵循慣例,編譯您的製作和測試程式碼會非常容易
-
將您的製作來源程式碼放在 src/main/java 目錄下
-
將您的測試來源程式碼放在 src/test/java 下
-
在
compileOnly
或implementation
組態中宣告您的製作編譯相依性(請參閱前一節) -
在
testCompileOnly
或testImplementation
組態中宣告您的測試編譯相依性 -
為製作程式碼執行
compileJava
任務,為測試執行compileTestJava
其他 JVM 語言外掛程式,例如 Groovy 的外掛程式,遵循相同的慣例模式。我們建議您盡可能遵循這些慣例,但您不必這麼做。有幾個自訂選項,您會在接下來看到。
自訂檔案和目錄位置
假設您有一個舊專案,它使用 src 目錄作為製作程式碼,並使用 test 作為測試程式碼。慣例目錄結構無法使用,因此您需要告訴 Gradle 在哪裡尋找來源檔案。您可以透過來源組態設定來執行此操作。
每個來源組態定義其來源程式碼所在的位置,以及資源和類別檔案的輸出目錄。您可以使用下列語法覆寫慣例值
sourceSets {
main {
java {
setSrcDirs(listOf("src"))
}
}
test {
java {
setSrcDirs(listOf("test"))
}
}
}
sourceSets {
main {
java {
srcDirs = ['src']
}
}
test {
java {
srcDirs = ['test']
}
}
}
現在,Gradle 只會直接在 src 和 test 中搜尋各自的來源程式碼。如果您不想覆寫慣例,但只是想新增一個額外的來源目錄,例如一個包含您想要分開保留的第三方來源程式碼的目錄,該怎麼辦?語法類似
sourceSets {
main {
java {
srcDir("thirdParty/src/main/java")
}
}
}
sourceSets {
main {
java {
srcDir 'thirdParty/src/main/java'
}
}
}
至關重要的是,我們在這裡使用 方法 srcDir()
來附加目錄路徑,而設定 srcDirs
屬性會取代任何現有值。這是 Gradle 中的常見慣例:設定屬性會取代值,而對應的方法會附加值。
您可以在 SourceSet 和 SourceDirectorySet 的 DSL 參考中看到所有可用的屬性和方法。請注意,srcDirs
和 srcDir()
都在 SourceDirectorySet
上。
變更編譯器選項
大多數編譯器選項都可以透過對應的任務存取,例如 compileJava
和 compileTestJava
。這些任務的類型為 JavaCompile,因此請閱讀任務參考以取得最新且完整的選項清單。
例如,如果您想要為編譯器使用個別的 JVM 程序,並防止編譯失敗導致建置失敗,您可以使用此組態
tasks.compileJava {
options.isIncremental = true
options.isFork = true
options.isFailOnError = false
}
compileJava {
options.incremental = true
options.fork = true
options.failOnError = false
}
您也可以這樣變更編譯器的詳細程度,停用位元組碼中的偵錯輸出,以及組態編譯器可以在哪裡找到註解處理器。
鎖定特定 Java 版本
預設情況下,Gradle 會將 Java 程式碼編譯成執行 Gradle 的 JVM 的語言層級。如果您需要在編譯時鎖定特定版本的 Java,Gradle 提供多個選項
使用工具鏈
當使用特定工具鏈編譯 Java 程式碼時,實際編譯是由指定 Java 版本的編譯器執行的。編譯器提供對所要求 Java 語言版本的語言功能和 JDK API 的存取。
在最簡單的情況下,可以使用 java
擴充功能為專案組態工具鏈。這樣一來,不僅編譯會受益,test
和 javadoc
等其他任務也會一致地使用相同的工具鏈。
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
您可以在 Java 工具鏈 指南中深入了解這方面的資訊。
使用 Java 發行版本
Gradle 支援使用 Java 10 的發行旗標。可以在編譯工作中設定如下。
tasks.compileJava {
options.release = 7
}
compileJava {
options.release = 7
}
發行旗標提供與工具鏈類似的保證。它驗證 Java 來源是否使用較新 Java 版本中引入的語言功能,以及程式碼是否存取較新 JDK 的 API。編譯器產生的位元組碼也對應於所要求的 Java 版本,表示已編譯的程式碼無法在較舊的 JVM 上執行。
Java 編譯器的 release
選項是在 Java 9 中引入的。但是,由於 Java 9 中的錯誤,只有從 Java 10 開始才能與 Gradle 一起使用此選項。
使用 Java 相容性選項
sourceCompatibility
和 targetCompatibility
選項對應於 Java 編譯器選項 -source
和 -target
。它們被視為鎖定特定 Java 版本的舊有機制。但是,這些選項無法防止使用較新 Java 版本中引入的 API。
sourceCompatibility
-
定義來源檔案中使用的 Java 語言版本。
targetCompatibility
-
定義程式碼應執行的最低 JVM 版本,亦即它決定編譯器產生的位元組碼版本。
這些選項可以設定為每個 JavaCompile 任務,或在 java { }
延伸模組中設定所有編譯任務,使用具有相同名稱的屬性。
針對 Java 6 和 Java 7
Gradle 本身只能在 Java 版本 8 或更高的 JVM 上執行。然而,Gradle 仍支援編譯、測試、產生 Javadoc 和執行 Java 6 和 Java 7 的應用程式。不支援 Java 5 和更早的版本。
如果使用 Java 10+,利用 release 旗標可能是比較簡單的解決方案,請參閱上方。
|
若要使用 Java 6 或 Java 7,需要設定下列任務
-
JavaCompile
任務以分岔並使用正確的 Java 主目錄 -
Javadoc
任務以使用正確的javadoc
可執行檔 -
Test
和JavaExec
任務以使用正確的java
可執行檔。
使用 Java 工具鏈時,可以執行下列操作
java {
toolchain {
languageVersion = JavaLanguageVersion.of(7)
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(7)
}
}
唯一的需求是安裝 Java 7,且必須在 Gradle 可以自動偵測的位置 或 明確設定。
分別編譯獨立的來源
大多數專案至少有兩組獨立的來源:生產程式碼和測試程式碼。Gradle 已將此場景納入其 Java 慣例中,但如果你有其他來源組怎麼辦?最常見的場景之一是當你以某種形式或其他形式進行獨立的整合測試時。在這種情況下,自訂來源組可能正是你需要的。
你可以在 Java 測試章節 中看到設定整合測試的完整範例。你可以用相同的方式設定其他來源組,以履行不同的角色。接下來的問題是:你應該在什麼時候定義自訂來源組?
若要回答這個問題,請考慮來源
-
是否需要使用唯一的類別路徑編譯
-
是否產生與
main
和test
不同的類別 -
是否形成專案的自然組成部分
如果你對 3 和其他任一個的回答都是肯定的,那麼自訂來源組可能是正確的方法。例如,整合測試通常是專案的一部分,因為它們會測試 main
中的程式碼。此外,它們通常有自己的相依性,獨立於 test
來源組,或者需要使用自訂 Test
任務執行。
其他常見的場景較不清晰,可能會有更好的解決方案。例如
-
分開 API 和實作 JAR — 將它們作為分開的專案可能比較合理,特別是如果你已經有複數專案建置
-
產生的來源 — 如果產生的來源應與生產程式碼一起編譯,請將其路徑新增至
main
來源組,並確保compileJava
任務取決於產生來源的任務
如果你不確定是否要建立自訂來源組,請繼續進行。這應該很簡單,如果沒有,那麼它可能不是這項工作的合適工具。
管理資源
許多 Java 專案使用來源檔案以外的資源,例如圖片、組態檔案和在地化資料。有時這些檔案只需要不變地封裝,有時需要作為範本檔案或以其他方式處理。無論如何,Java 函式庫外掛都會為每個來源組新增一個特定的 複製 任務,用於處理其關聯資源。
任務名稱遵循 processSourceSetResources
的慣例 — 或 main
來源組的 processResources
— 它會自動將 src/[sourceSet]/resources 中的任何檔案複製到會包含在生產 JAR 中的目錄。此目標目錄也會包含在測試的執行時期類別路徑中。
由於 processResources
是 ProcessResources
任務的一個執行個體,因此你可以執行 使用檔案 章節中所述的任何處理。
Java 屬性檔案和可重製建置
用於寫入屬性檔案的標準 Java API 會在每次使用相同的屬性和值時產生一個唯一的檔案,因為它在註解中包含時間戳記。如果沒有任何屬性變更,Gradle 的 寫入屬性
任務會產生完全相同的輸出位元組。這是透過對屬性檔案的產生方式進行一些調整來達成的
-
輸出中未新增時間戳記註解
-
行分隔符號與系統無關,但可以明確組態(預設為
'\n'
) -
屬性會依字母順序排列
有時,您可能希望在不同的機器上以逐位元組的方式重新建立檔案。您希望確保從原始碼建立人工製品會產生相同的結果,無論何時何地建立。這是可重製建置組織等專案的必要條件。
這些調整不僅能帶來更好的增量建置整合,還能協助可重製建置。基本上,可重製建置保證您將看到建置執行的相同結果,包括測試結果和生產二進位檔,無論您何時或在什麼系統上執行。
執行測試
除了提供在 src/test/java 中自動編譯單元測試外,Java 函式庫外掛程式還原生支援使用 JUnit 3、4 和 5(JUnit 5 支援在 Gradle 4.6 中推出)和 TestNG 執行的測試。您會取得
-
使用
test
來源組的類型為 Test 的自動test
工作 -
包含執行 所有
Test
工作結果的 HTML 測試報告 -
輕鬆篩選要執行的測試
-
精細控制測試執行的模式
-
建立自己的測試執行和測試報告工作的機會
您不會為宣告的每個來源組取得 Test
工作,因為並非每個來源組都代表測試!這就是為什麼您通常需要建立自己的 Test
工作,以處理整合和驗收測試等事項,如果無法包含在 test
來源組中。
由於在測試方面有很多內容需要涵蓋,因此主題在專屬章節中,我們會探討
-
測試如何執行
-
如何透過篩選執行測試子集
-
Gradle 如何發現測試
-
如何設定測試報告並新增自己的報告工作
-
如何使用特定的 JUnit 和 TestNG 功能
您也可以在 Test 的 DSL 參考中深入了解如何設定測試。
封裝和發佈
封裝和潛在發佈 Java 專案的方式取決於專案的類型。函式庫、應用程式、Web 應用程式和企業應用程式都有不同的需求。在本節中,我們將專注於 Java 函式庫外掛程式提供的基本架構。
預設情況下,Java 函式庫外掛程式提供 jar
工作,將所有編譯的生產類別和資源封裝到單一 JAR 中。此 JAR 也會由 assemble
工作自動建置。此外,可以設定外掛程式以提供 javadocJar
和 sourcesJar
工作,以封裝 Javadoc 和原始碼(如果需要)。如果使用發佈外掛程式,這些工作將在發佈期間自動執行,或可以直接呼叫。
java {
withJavadocJar()
withSourcesJar()
}
java {
withJavadocJar()
withSourcesJar()
}
如果您要建立「uber」(又稱「fat」)JAR,可以使用類似這樣的任務定義
plugins {
java
}
version = "1.0.0"
repositories {
mavenCentral()
}
dependencies {
implementation("commons-io:commons-io:2.6")
}
tasks.register<Jar>("uberJar") {
archiveClassifier = "uber"
from(sourceSets.main.get().output)
dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
})
}
plugins {
id 'java'
}
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
implementation 'commons-io:commons-io:2.6'
}
tasks.register('uberJar', Jar) {
archiveClassifier = 'uber'
from sourceSets.main.output
dependsOn configurations.runtimeClasspath
from {
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
}
}
請參閱 Jar,以取得可用的設定選項的更多詳細資料。請注意,您需要在此處使用 archiveClassifier
,而不是 archiveAppendix
,才能正確發布 JAR。
您可以使用其中一個發布外掛程式來發布 Java 專案建立的 JAR
修改 JAR 清單
Jar
、War
和 Ear
任務的每個執行個體都有 manifest
屬性,讓您可以自訂對應的封存檔中的 MANIFEST.MF 檔案。下列範例示範如何設定 JAR 清單中的屬性
tasks.jar {
manifest {
attributes(
"Implementation-Title" to "Gradle",
"Implementation-Version" to archiveVersion
)
}
}
jar {
manifest {
attributes("Implementation-Title": "Gradle",
"Implementation-Version": archiveVersion)
}
}
請參閱 Manifest,以取得它提供的設定選項。
您也可以建立 Manifest
的獨立執行個體。這樣做的原因之一是在 JAR 之間共用清單資訊。下列範例示範如何在 JAR 之間共用共用屬性
val sharedManifest = java.manifest {
attributes (
"Implementation-Title" to "Gradle",
"Implementation-Version" to version
)
}
tasks.register<Jar>("fooJar") {
manifest = java.manifest {
from(sharedManifest)
}
}
def sharedManifest = java.manifest {
attributes("Implementation-Title": "Gradle",
"Implementation-Version": version)
}
tasks.register('fooJar', Jar) {
manifest = java.manifest {
from sharedManifest
}
}
您還可以使用的另一個選項是將清單合併成單一的 Manifest
物件。這些來源清單可以是文字格式或另一個 Manifest
物件。在下列範例中,來源清單都是文字檔案,但 sharedManifest
除外,它是前一個範例中的 Manifest
物件
tasks.register<Jar>("barJar") {
manifest {
attributes("key1" to "value1")
from(sharedManifest, "src/config/basemanifest.txt")
from(listOf("src/config/javabasemanifest.txt", "src/config/libbasemanifest.txt")) {
eachEntry(Action<ManifestMergeDetails> {
if (baseValue != mergeValue) {
value = baseValue
}
if (key == "foo") {
exclude()
}
})
}
}
}
tasks.register('barJar', Jar) {
manifest {
attributes key1: 'value1'
from sharedManifest, 'src/config/basemanifest.txt'
from(['src/config/javabasemanifest.txt', 'src/config/libbasemanifest.txt']) {
eachEntry { details ->
if (details.baseValue != details.mergeValue) {
details.value = baseValue
}
if (details.key == 'foo') {
details.exclude()
}
}
}
}
}
清單會按照在 from
陳述式中宣告的順序進行合併。如果基本清單和合併的清單都為同一個金鑰定義值,預設情況下,合併的清單會獲勝。您可以透過新增 eachEntry
動作,在其中存取結果清單的每個輸入的 ManifestMergeDetails 執行個體,來完全自訂合併行為。請注意,合併會延遲執行,也就是在產生 JAR 或呼叫 Manifest.writeTo()
或 Manifest.getEffectiveManifest()
時執行。
談到 writeTo()
,你可以用它在任何時候輕鬆地將明細寫入磁碟,如下所示
tasks.jar { manifest.writeTo(layout.buildDirectory.file("mymanifest.mf")) }
tasks.named('jar') { manifest.writeTo(layout.buildDirectory.file('mymanifest.mf')) }
產生 API 文件
Java Library Plugin 提供一個 Javadoc 類型的 javadoc
任務,它會為所有製作程式碼產生標準 Javadoc,也就是 main
來源組中的任何來源。此任務支援 Javadoc 參考文件 中所述的核心 Javadoc 和標準 doclet 選項。請參閱 CoreJavadocOptions 和 StandardJavadocDocletOptions 以取得這些選項的完整清單。
以下是一個你可以執行的範例,假設你想要在 Javadoc 註解中使用 Asciidoc 語法。為此,你需要將 Asciidoclet 加入 Javadoc 的 doclet 路徑。以下是一個執行此動作的範例
val asciidoclet by configurations.creating
dependencies {
asciidoclet("org.asciidoctor:asciidoclet:1.+")
}
tasks.register("configureJavadoc") {
doLast {
tasks.javadoc {
options.doclet = "org.asciidoctor.Asciidoclet"
options.docletpath = asciidoclet.files.toList()
}
}
}
tasks.javadoc {
dependsOn("configureJavadoc")
}
configurations {
asciidoclet
}
dependencies {
asciidoclet 'org.asciidoctor:asciidoclet:1.+'
}
tasks.register('configureJavadoc') {
doLast {
javadoc {
options.doclet = 'org.asciidoctor.Asciidoclet'
options.docletpath = configurations.asciidoclet.files.toList()
}
}
}
javadoc {
dependsOn configureJavadoc
}
你不需要為此建立設定檔,但這是一個處理特定目的所需相依項的優雅方式。
你可能也想要建立自己的 Javadoc 任務,例如為測試產生 API 文件
tasks.register<Javadoc>("testJavadoc") {
source = sourceSets.test.get().allJava
}
tasks.register('testJavadoc', Javadoc) {
source = sourceSets.test.allJava
}
這些只是你可能會遇到的兩個非平凡但常見的自訂範例。
清除建置
Java Library Plugin 會透過套用 Base Plugin 將 clean
任務加入你的專案。此任務只是刪除 layout.buildDirectory
目錄中的所有內容,因此你應該始終將建置產生的檔案放在那裡。此任務是 Delete 的一個執行個體,你可以透過設定其 dir
屬性來變更它所刪除的目錄。
建置 JVM 元件
所有特定的 JVM 外掛程式都是建立在 Java 外掛程式 之上。上述範例僅說明此基本外掛程式提供的概念,並與所有 JVM 外掛程式共用。
繼續閱讀以了解哪些外掛程式適合哪些專案類型,建議選擇特定外掛程式,而不是直接套用 Java 外掛程式。
建立 Java 函式庫
函式庫專案的獨特之處在於它們被其他 Java 專案使用(或「消耗」)。這表示與 JAR 檔案一起發布的相依性資料(通常採用 Maven POM 形式)至關重要。特別是,函式庫的使用者應該能夠區分兩種不同類型的相依性:僅用於編譯函式庫的相依性,以及用於編譯使用者的相依性。
Gradle 透過 Java 函式庫外掛程式 來管理此區別,除了本章中介紹的 implementation 組態之外,還引入了 api 組態。如果相依性的類型出現在函式庫公用類別的公用欄位或方法中,則該相依性會透過函式庫的公用 API 公開,因此應將其新增至 api 組態。否則,相依性是內部實作細節,應新增至 implementation。
如果您不確定 API 和實作相依性之間的差異,Java 函式庫外掛程式章節 有詳細的說明。此外,您可以探索建立 Java 函式庫的基本實務 範例。
建立 Java 網路應用程式
Java 網路應用程式可以透過多種方式進行封裝和部署,視您使用的技術而定。例如,您可能會使用 Spring Boot 搭配 fat JAR 或是在 Netty 上執行的 Reactive 系統。無論您使用哪一種技術,Gradle 及其龐大的外掛程式社群都能滿足您的需求。不過,核心 Gradle 只直接支援部署為 WAR 檔案的傳統 Servlet 網路應用程式。
該支援是透過 War 外掛程式 提供的,它會自動套用 Java 外掛程式,並新增一個額外的封裝步驟,執行下列動作
-
將靜態資源從 src/main/webapp 複製到 WAR 的根目錄
-
將編譯的生產類別複製到 WAR 的 WEB-INF/classes 子目錄
-
將函式庫相依性複製到 WAR 的 WEB-INF/lib 子目錄
這是由 war
任務完成的,它有效地取代了 jar
任務(雖然該任務仍然存在),並附加到 assemble
生命週期任務。請參閱外掛程式的章節,以取得更多詳細資料和組態選項。
沒有核心支援可直接從建置執行您的網路應用程式,但我們建議您嘗試 Gretty 社群外掛程式,它提供內嵌的 Servlet 容器。
建置 Java EE 應用程式
多年來,Java 企業系統已經改變了很多,但如果您仍在部署到 JEE 應用程式伺服器,您可以使用 Ear 外掛程式。這會新增慣例和一個建置 EAR 檔案的任務。外掛程式的章節有更多詳細資料。
建置 Java 平台
Java 平台代表一組相依性宣告和約束,它們形成一個要套用在使用專案上的緊密結合單元。平台沒有來源,也沒有自己的成品。它在 Maven 世界中對應到 BOM。
該支援是透過 Java Platform 外掛程式 提供的,它會設定不同的組態和發布元件。
此外掛程式是一個例外,因為它不會套用 Java 外掛程式。 |
啟用 Java 預覽功能
使用 Java 預覽功能很可能會讓您的程式碼與未預覽功能編譯的程式碼不相容。因此,我們強烈建議您不要發布使用預覽功能編譯的程式庫,並將預覽功能的使用限制在玩具專案。 |
要針對編譯、測試執行和執行時間啟用 Java 預覽功能,您可以使用以下 DSL 片段
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("--enable-preview")
}
tasks.withType<Test>().configureEach {
jvmArgs("--enable-preview")
}
tasks.withType<JavaExec>().configureEach {
jvmArgs("--enable-preview")
}
tasks.withType(JavaCompile).configureEach {
options.compilerArgs += "--enable-preview"
}
tasks.withType(Test).configureEach {
jvmArgs += "--enable-preview"
}
tasks.withType(JavaExec).configureEach {
jvmArgs += "--enable-preview"
}
建置其他 JVM 語言專案
如果您想利用 JVM 的多語言面向,這裡所描述的大部分內容仍然適用。
語言之間的編譯相依性
這些外掛程式會在 Groovy/Scala 編譯和 Java 編譯(來源集的 java
資料夾中的來源程式碼)之間建立相依性。您可以透過調整所涉及編譯任務的類別路徑來變更此預設行為,如下列範例所示
tasks.named<AbstractCompile>("compileGroovy") {
// Groovy only needs the declared dependencies
// (and not longer the output of compileJava)
classpath = sourceSets.main.get().compileClasspath
}
tasks.named<AbstractCompile>("compileJava") {
// Java also depends on the result of Groovy compilation
// (which automatically makes it depend of compileGroovy)
classpath += files(sourceSets.main.get().groovy.classesDirectory)
}
tasks.named('compileGroovy') {
// Groovy only needs the declared dependencies
// (and not longer the output of compileJava)
classpath = sourceSets.main.compileClasspath
}
tasks.named('compileJava') {
// Java also depends on the result of Groovy compilation
// (which automatically makes it depend of compileGroovy)
classpath += files(sourceSets.main.groovy.classesDirectory)
}
-
透過將
compileGroovy
類別路徑設定為僅sourceSets.main.compileClasspath
,我們有效地移除先前對compileJava
的相依性,而該相依性是透過讓類別路徑也考量sourceSets.main.java.classesDirectory
來宣告的 -
透過將
sourceSets.main.groovy.classesDirectory
加入compileJava
classpath
,我們有效地宣告對compileGroovy
任務的相依性
這一切都是透過使用 目錄屬性 來實現的。