從 Gradle 4.0 開始,此組建工具完全支援快取純粹 Java 專案。內建任務可編譯、測試、記錄和檢查 Java 程式碼品質,並支援內建的組建快取。

Java 編譯

快取 Java 編譯會利用 Gradle 深入瞭解編譯類別路徑。此機制 避免重新編譯,當相依項變更時,不會影響其應用程式二進位介面 (ABI)。由於快取金鑰僅受相依項的 ABI 影響(不受其實作詳細資料影響,例如私人類型和方法主體),因此,如果任務輸出快取是由相同來源和 ABI 等效相依項所產生,則任務輸出快取也可以重複使用已編譯的類別。

例如,假設有一個專案包含兩個模組:應用程式相依於函式庫。假設 CI 已組建最新版本並上傳至共用快取。如果開發人員現在修改函式庫中的方法主體,則需要在他們的電腦上重新組建函式庫。但是,他們將能夠從共用快取載入應用程式的已編譯類別。Gradle 可以執行此動作,因為用於在 CI 上編譯應用程式的函式庫和在本地端可用的已修改函式庫共用相同的 ABI。

註解處理器

編譯避免開箱即用。不過有一個注意事項:在使用註解處理器時,Gradle 會使用註解處理器類別路徑作為輸入。與大多數編譯相依性(其中只有 ABI 會影響編譯)不同,註解處理器的實作必須視為編譯器的輸入。因此,Gradle 會將註解處理器視為執行時期類別路徑,這表示在那裡進行的輸入常態化較少。如果 Gradle 在編譯類別路徑上偵測到註解處理器,則在未明確設定時,註解處理器類別路徑會預設為編譯類別路徑,這表示整個編譯類別路徑會被視為執行時期類別路徑輸入。

對於上述範例,這表示從編譯類別路徑中萃取的 ABI 將保持不變,但註解處理器類別路徑(因為它未以編譯避免處理)會有所不同。最後,開發人員必須重新編譯應用程式。

避免這種效能損失最簡單的方法是不使用註解處理器。不過,如果您需要使用它們,請務必明確設定註解處理器類別路徑,只包含註解處理所需的函式庫。Java 編譯避免區段說明如何執行此操作。

一些常見的 Java 相依性(例如 Log4j 2.x)會與註解處理器綑綁在一起。如果您使用這些相依性,但未利用綑綁註解處理器的功能,最好完全停用註解處理。這可透過將註解處理器類別路徑設定為空集合來完成。

單元測試執行

用於 JVM 語言測試執行的Test工作會對其類別路徑採用執行時期類別路徑常態化。這表示測試類別路徑上 jar 中的順序和時間戳記的變更不會導致工作過期或變更建置快取金鑰。為了達成穩定的工作輸入,您也可以運用過濾執行時期類別路徑的力量。

整合測試執行

單元測試很容易快取,因為它們通常沒有外部相依性。整合測試的情況可能截然不同,因為它們可能依賴於測試和生產程式碼以外的各種輸入。這些外部因素可以是,例如

  • 作業系統類型和版本,

  • 為測試安裝的外部工具,

  • 環境變數和 Java 系統屬性,

  • 其他服務已啟動並執行,

  • 測試軟體的發行版本。

您需要小心為整合測試宣告這些額外的輸入,以避免不正確的快取命中。例如,將 Gradle 使用中的作業系統宣告為名為integTestTest工作的輸入,其運作方式如下

build.gradle.kts
tasks.integTest {
    inputs.property("operatingSystem") {
        System.getProperty("os.name")
    }
}
build.gradle
tasks.named('integTest') {
    inputs.property("operatingSystem") {
        System.getProperty("os.name")
    }
}

檔案作為輸入

整合測試通常會依賴於封裝的應用程式。如果這是一個 zip 或 tar 檔案,將其新增為整合測試任務的輸入可能會導致快取遺漏。這是因為,如 可重複任務輸出 中所述,重新建置檔案通常會變更檔案中的元資料。您可以依賴於檔案的展開內容。另請參閱處理 不可重複輸出 的部分。

處理檔案路徑

您可能會使用系統屬性從建置環境傳遞一些資訊到整合測試任務。傳遞絕對路徑會中斷整合測試任務的 可重新定位性

build.gradle.kts
// Don't do this! Breaks relocatability!
tasks.integTest {
    systemProperty("distribution.location", layout.buildDirectory.dir("dist").get().asFile.absolutePath)
}
build.gradle
// Don't do this! Breaks relocatability!
tasks.named('integTest') {
    systemProperty "distribution.location", layout.buildDirectory.dir('dist').get().asFile.absolutePath
}

您可以新增註解的 CommandLineArgumentProviderintegTest 任務,而不是直接新增絕對路徑作為系統屬性。

build.gradle.kts
abstract class DistributionLocationProvider : CommandLineArgumentProvider {  (1)
    @get:InputDirectory
    @get:PathSensitive(PathSensitivity.RELATIVE)  (2)
    abstract val distribution: DirectoryProperty

    override fun asArguments(): Iterable<String> =
        listOf("-Ddistribution.location=${distribution.get().asFile.absolutePath}")  (3)
}

tasks.integTest {
    jvmArgumentProviders.add(
        objects.newInstance<DistributionLocationProvider>().apply {  (4)
            distribution = layout.buildDirectory.dir("dist")
        }
    )
}
build.gradle
abstract class DistributionLocationProvider implements CommandLineArgumentProvider {  (1)
    @InputDirectory
    @PathSensitive(PathSensitivity.RELATIVE)  (2)
    abstract DirectoryProperty getDistribution()

    @Override
    Iterable<String> asArguments() {
        ["-Ddistribution.location=${distribution.get().asFile.absolutePath}"]  (3)
    }
}

tasks.named('integTest') {
    jvmArgumentProviders.add(
        objects.newInstance(DistributionLocationProvider).tap {  (4)
            distribution = layout.buildDirectory.dir('dist')
        }
    )
}
1 建立一個實作 CommandLineArgumentProvider 的類別。
2 宣告輸入和輸出,並具有對應的路徑敏感性。
3 asArguments 需要傳回 JVM 引數,將所需的系統屬性傳遞到測試 JVM。
4 將新建立的類別的執行個體新增為 JVM 引數提供者到整合測試任務。[1]

忽略系統屬性

可能需要忽略一些系統屬性作為輸入,因為它們不會影響整合測試的結果。為此,請將 CommandLineArgumentProvider 新增到 integTest 任務。

build.gradle.kts
abstract class CiEnvironmentProvider : CommandLineArgumentProvider {
    @get:Internal  (1)
    abstract val agentNumber: Property<String>

    override fun asArguments(): Iterable<String> =
        listOf("-DagentNumber=${agentNumber.get()}")  (2)
}

tasks.integTest {
    jvmArgumentProviders.add(
        objects.newInstance<CiEnvironmentProvider>().apply {  (3)
            agentNumber = providers.environmentVariable("AGENT_NUMBER").orElse("1")
        }
    )
}
build.gradle
abstract class CiEnvironmentProvider implements CommandLineArgumentProvider {
    @Internal  (1)
    abstract Property<String> getAgentNumber()

    @Override
    Iterable<String> asArguments() {
        ["-DagentNumber=${agentNumber.get()}"]  (2)
    }
}

tasks.named('integTest') {
    jvmArgumentProviders.add(
        objects.newInstance(CiEnvironmentProvider).tap {  (3)
            agentNumber = providers.environmentVariable("AGENT_NUMBER").orElse("1")
        }
    )
}
1 @Internal 表示此屬性不會影響整合測試的輸出。
2 實際測試執行的系統屬性。
3 將新建立的類別的執行個體新增為 JVM 引數提供者到整合測試任務。[1]

1。此範例中的 CommandLineArgumentProvider 是以 管理類型 實作。