Gradle 包含一個高度複雜的相依性快取機制,旨在盡可能減少相依性解析中發出的遠端請求數量,同時力求保證相依性解析的結果是正確且可重現的。

  1. 本地快取:Gradle 在本地快取相依性,以避免重複下載。快取位於使用者主資料夾下的 .gradle 目錄中(例如,~/.gradle/caches/modules-2)。當請求相依性時,Gradle 會先檢查此本地快取,然後才嘗試從遠端儲存庫擷取。

  2. 變更相依性:預設情況下,Gradle 會區別對待標記為「變更中」的相依性(例如,SNAPSHOT 或動態相依性),並更頻繁地重新整理它們。這些相依性的快取時間可以透過程式設計方式更改。

  3. 離線模式:Gradle 可以在離線模式下執行,僅使用快取的相依性,而無需嘗試從遠端儲存庫下載任何內容。您可以使用 --offline 標誌啟用離線模式,確保您的建置僅使用快取的 Artifact。

  4. 重新整理相依性:若要強制 Gradle 更新其相依性,請使用 --refresh-dependencies 標誌。此選項指示 Gradle 略過快取,並檢查遠端儲存庫中更新的 Artifact。Gradle 會下載它們,但僅在偵測到變更時才會下載,並使用雜湊來避免不必要的下載。

1. 相依性快取

Gradle 相依性快取包含兩種儲存類型,位於 $GRADLE_USER_HOME/caches

  1. 已下載 Artifact 的基於檔案的儲存,包括二進制檔案(如 jar 檔案)以及原始下載的中繼資料(如 POM 檔案和 Ivy 檔案)。Artifact 儲存在總和檢查碼下,因此名稱衝突不會導致問題。

  2. 已解析模組中繼資料的二進制儲存,包括解析動態版本、模組描述符和 Artifact 的結果。

獨立的中繼資料快取

Gradle 將相依性解析的各個方面以二進制格式記錄在中繼資料快取中。

中繼資料快取中儲存的資訊包括

  • 將動態版本(例如 1.+)解析為具體版本(例如 1.2)的結果。

  • 特定模組的已解析模組中繼資料,包括模組 Artifact 和模組相依性。

  • 特定 Artifact 的已解析 Artifact 中繼資料,包括指向已下載 Artifact 檔案的指標。

  • 特定儲存庫中不存在特定模組或 Artifact 的記錄,從而消除了重複嘗試存取不存在的資源。

中繼資料快取中的每個條目都包含提供資訊的儲存庫的記錄,以及可用於快取過期的時間戳記。

儲存庫快取是獨立的

如上所述,每個儲存庫都有一個獨立的中繼資料快取。儲存庫由其 URL、類型和佈局識別。

如果模組或 Artifact 尚未從此儲存庫解析,Gradle 將嘗試針對該儲存庫解析模組。這始終會涉及對儲存庫的遠端查找,但在許多情況下,不需要下載

如果所需的 Artifact 在最初解析它們的儲存庫中不可用,相依性解析將會失敗。一旦從特定儲存庫解析,Artifact 就會變得「黏著」,這表示 Gradle 將避免從其他儲存庫解析它們,以防止 Artifact 來源發生意外或潛在的不安全變更。這可確保跨環境的一致性,但如果機器之間的儲存庫不同,也可能導致失敗。

儲存庫獨立性允許建置彼此隔離。這是建立在任何環境中都可靠且可重現的建置的關鍵功能。

Artifact 重用

在下載 Artifact 之前,Gradle 會嘗試透過下載相關聯的 .sha512.sha256.sha1.md5 檔案(依序嘗試每個檔案)來擷取 Artifact 的總和檢查碼。

如果總和檢查碼可用,如果已存在具有相同 ID 和總和檢查碼的 Artifact,Gradle 將跳過下載。但是,如果無法從遠端伺服器擷取總和檢查碼,Gradle 將繼續下載 Artifact,但如果它與現有的 Artifact 匹配,則會忽略它。

Gradle 也嘗試從本地 Maven 儲存庫重用 Artifact。如果先前由 Maven 下載的 Artifact 是匹配項,Gradle 將使用它,前提是它可以根據遠端伺服器的總和檢查碼進行驗證。

基於總和檢查碼的儲存

不同的儲存庫可能會提供不同的二進制 Artifact 以回應相同的 Artifact 識別符。

Maven SNAPSHOT Artifact 通常是這種情況,但對於任何未變更其識別符就重新發布的 Artifact 也是如此。透過基於總和檢查碼快取 Artifact,Gradle 能夠維護同一 Artifact 的多個版本。這表示在針對一個儲存庫解析時,Gradle 永遠不會覆寫來自不同儲存庫的快取 Artifact 檔案。這是無需每個儲存庫都使用單獨的 Artifact 檔案儲存即可完成的。

快取鎖定

Gradle 相依性快取使用基於檔案的鎖定,以確保它可以由多個 Gradle 程序同時安全地使用。每當讀取或寫入二進制中繼資料儲存時,都會持有鎖定,但對於緩慢的操作(例如下載遠端 Artifact)會釋放鎖定。

僅當不同的 Gradle 程序可以彼此通信時,才支援此並行存取。對於容器化建置,通常並非如此

快取清理

Gradle 會追蹤相依性快取中哪些 Artifact 被存取。根據此資訊,快取會定期掃描(每 24 小時不超過一次),以識別超過 30 天未使用過的 Artifact。然後會刪除這些過時的 Artifact,以防止快取無限期地增長。

您可以在Gradle 管理的目錄中了解有關快取清理的更多資訊。

2. 變更相依性

Gradle 對待標記為「變更中」的相依性(例如 SNAPSHOT 相依性)與常規相依性不同,更頻繁地重新整理它們,以確保您始終使用最新版本。

若要將相依性宣告為變更中,您可以在相依性宣告中設定 changing = true 屬性。這對於預期會頻繁變更而沒有新版本號的相依性很有用

dependencies {
    implementation("com.example:some-library:1.0-SNAPSHOT") // Automatically gets treated as changing
    implementation("com.example:my-library:1.0") {  // Must be explicitly set as changing
        changing = true
    }
}

快取變更相依性

預設情況下,Gradle 會快取這些相依性(包括動態版本和變更中的模組)24 小時,這表示在此期間它不會聯繫遠端儲存庫以獲取新版本。

若要讓 Gradle 更頻繁地或在每次建置時檢查較新版本,您可以相應地調整快取閾值或存活時間 (TTL) 設定。

為動態或變更中的版本使用較短的 TTL 閾值可能會由於遠端儲存庫存取次數增加而導致建置時間更長。

您可以使用組態的 ResolutionStrategy 以程式設計方式微調快取的某些方面。如果您想永久更改設定,則程式設計方法很有用。

若要變更 Gradle 快取動態版本的已解析版本的時間長度,請使用

build.gradle.kts
configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor(10, "minutes")
}
build.gradle
configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
}

若要變更 Gradle 快取變更中模組的中繼資料和 Artifact 的時間長度,請使用

build.gradle.kts
configurations.all {
    resolutionStrategy.cacheChangingModulesFor(4, "hours")
}
build.gradle
configurations.all {
    resolutionStrategy.cacheChangingModulesFor 4, 'hours'
}

3. 使用離線模式

--offline 命令列切換指示 Gradle 從快取中使用相依性模組,無論它們是否到期再次檢查。在 offline 模式下執行時,Gradle 將不會嘗試存取網路以進行相依性解析。如果所需的模組不在相依性快取中,則建置將會失敗。

4. 強制重新整理相依性

您可以從命令列控制特定建置調用的相依性快取行為。命令列選項有助於為單個建置執行做出有選擇性的、臨機的選擇。

有時,Gradle 相依性快取可能會與已配置儲存庫的實際狀態不同步。可能是儲存庫最初配置錯誤,或者可能錯誤地發布了「非變更中」的模組。若要重新整理相依性快取中的所有相依性,請在命令列上使用 --refresh-dependencies 選項。

--refresh-dependencies 選項告訴 Gradle 忽略已解析模組和 Artifact 的所有快取條目。將針對所有已配置的儲存庫執行新的解析,重新計算動態版本、重新整理模組並下載 Artifact。但是,在可能的情況下,Gradle 會先檢查先前下載的 Artifact 是否有效,然後再重新下載。這是透過比較儲存庫中已發布的總和檢查碼值與現有已下載 Artifact 的總和檢查碼值來完成的。

重新整理相依性將導致 Gradle 使其清單快取失效。但是

  • 它將對中繼資料檔案執行 HTTP HEAD 請求,但如果它們相同,將不會重新下載它們

  • 它將對 Artifact 檔案執行 HTTP HEAD 請求,但如果它們相同,將不會重新下載它們

換句話說,重新整理相依性在您實際使用動態相依性您有未意識到的變更相依性時才會產生影響(在這種情況下,您有責任將它們正確地宣告為 Gradle 的變更相依性)。

認為使用 --refresh-dependencies 會強制下載相依性是一種常見的誤解。情況並非如此:Gradle 只會執行嚴格要求重新整理動態相依性的操作。這可能涉及下載新的清單、中繼資料檔案,甚至 Artifact,但如果沒有任何變更,則影響最小。

處理臨時建置

在臨時容器中執行建置是一種常見的做法。容器通常僅在執行單個建置後才會產生,然後銷毀。當建置依賴於許多每個容器都必須重新下載的相依性時,這可能會成為一個實際問題。為了幫助解決這種情況,Gradle 提供了幾個選項

複製和重用快取

相依性快取(檔案和中繼資料部分)都完全使用相對路徑編碼。這表示完全有可能複製快取並看到 Gradle 從中受益。

可以複製的路徑是 $GRADLE_USER_HOME/caches/modules-<version>。唯一的限制是以相同的結構將其放置在目的地,其中 GRADLE_USER_HOME 的值可能不同。

如果 *.lockgc.properties 檔案存在,請勿複製它們。

請注意,建立和使用快取應使用相容的 Gradle 版本完成,如下表所示。否則,建置可能仍然需要與遠端儲存庫進行一些交互以完成遺失的資訊,這些資訊可能在不同的版本中可用。如果有多個不相容的 Gradle 版本在運行,則在播種快取時應全部使用。

模組快取版本 檔案快取版本 中繼資料快取版本 Gradle 版本

modules-2

files-2.1

metadata-2.95

Gradle 6.1 至 Gradle 6.3

modules-2

files-2.1

metadata-2.96

Gradle 6.4 至 Gradle 6.7

modules-2

files-2.1

metadata-2.97

Gradle 6.8 至 Gradle 7.4

modules-2

files-2.1

metadata-2.99

Gradle 7.5 至 Gradle 7.6.1

modules-2

files-2.1

metadata-2.101

Gradle 7.6.2

modules-2

files-2.1

metadata-2.100

Gradle 8.0

modules-2

files-2.1

metadata-2.105

Gradle 8.1

modules-2

files-2.1

metadata-2.106

Gradle 8.2 至 Gradle 8.10.2

modules-2

files-2.1

metadata-2.107

Gradle 8.11 及更高版本

與其他 Gradle 實例共用相依性快取

除了將相依性快取複製到每個容器中之外,還可以掛載一個共用的唯讀目錄,該目錄將充當所有容器的相依性快取。與傳統的相依性快取不同,此快取在沒有鎖定的情況下被存取,從而使多個建置可以同時從快取中讀取。重要的是,當其他建置可能從唯讀快取中讀取時,不要寫入唯讀快取。

使用共用的唯讀快取時,Gradle 會在本地 Gradle 使用者主目錄中的可寫快取和共用的唯讀快取中查找相依性(Artifact 或中繼資料)。如果唯讀快取中存在相依性,則不會下載。如果唯讀快取中缺少相依性,則會下載並新增至可寫快取。實際上,這表示可寫快取僅包含唯讀快取中不可用的相依性。

唯讀快取應來自已包含某些所需相依性的 Gradle 相依性快取。快取可能不完整;但是,空的共用快取只會增加額外負荷。

共用的唯讀相依性快取是一項孵化中的功能。

使用共用相依性快取的第一步是透過複製現有的本地快取來建立一個。為此,您需要遵循上面的說明

然後設定 GRADLE_RO_DEP_CACHE 環境變數以指向包含快取的目錄

$GRADLE_RO_DEP_CACHE
   |-- modules-2 : the read-only dependency cache, should be mounted with read-only privileges

$GRADLE_HOME
   |-- caches
         |-- modules-2 : the container specific dependency cache, should be writable
         |-- ...
   |-- ...

在 CI 環境中,最好有一個建置來「播種」Gradle 相依性快取,然後將其複製到不同的目錄或分發,例如,作為 Docker 卷。然後,此目錄可以用作其他建置的唯讀快取。您不應使用現有的 Gradle 安裝快取作為唯讀快取,因為此目錄可能包含鎖定並且可能被播種建置修改。