封裝可執行歸檔文件

此外掛程式可以建立包含應用程式所有依賴項的可執行歸檔文件 (jar 檔和 war 檔),然後可以使用 java -jar 執行。

封裝可執行 Jar

可執行 jar 可以使用 bootJar 任務來建置。當套用 java 外掛程式時,此任務會自動建立,並且是 BootJar 的一個實例。assemble 任務會自動組態為依賴 bootJar 任務,因此執行 assemble (或 build) 也會執行 bootJar 任務。

封裝可執行 War

可執行 war 可以使用 bootWar 任務來建置。當套用 war 外掛程式時,此任務會自動建立,並且是 BootWar 的一個實例。assemble 任務會自動組態為依賴 bootWar 任務,因此執行 assemble (或 build) 也會執行 bootWar 任務。

封裝可執行和可部署的 War

可以封裝 war 檔,使其可以使用 java -jar 執行並部署到外部容器。若要執行此操作,應將嵌入式 Servlet 容器依賴項新增至 providedRuntime 組態,例如

  • Groovy

  • Kotlin

dependencies {
	implementation('org.springframework.boot:spring-boot-starter-web')
	providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')
}
dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
}

這可確保它們被封裝在 war 檔的 WEB-INF/lib-provided 目錄中,從而不會與外部容器自身的類別衝突。

相較於 Gradle 的 compileOnly 組態,providedRuntime 是更佳的選擇,因為除了其他限制之外,compileOnly 依賴項不在測試類別路徑上,因此任何基於 Web 的整合測試都會失敗。

封裝可執行和純歸檔文件

預設情況下,當組態 bootJarbootWar 任務時,jarwar 任務會組態為使用 plain 作為其歸檔文件分類器的慣例。這可確保 bootJarjarbootWarwar 具有不同的輸出位置,從而允許同時建置可執行歸檔文件和純歸檔文件。

如果您偏好可執行歸檔文件 (而不是純歸檔文件) 使用分類器,請組態分類器,如下列 jarbootJar 任務的範例所示

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	archiveClassifier = 'boot'
}

tasks.named("jar") {
	archiveClassifier = ''
}
tasks.named<BootJar>("bootJar") {
	archiveClassifier.set("boot")
}

tasks.named<Jar>("jar") {
	archiveClassifier.set("")
}

或者,如果您偏好完全不建置純歸檔文件,請停用其任務,如下列 jar 任務的範例所示

  • Groovy

  • Kotlin

tasks.named("jar") {
	enabled = false
}
tasks.named<Jar>("jar") {
	enabled = false
}
在建立 Native Images 時,請勿停用 jar 任務。有關詳細資訊,請參閱 #33238

組態可執行歸檔文件封裝

BootJarBootWar 任務分別是 Gradle 的 JarWar 任務的子類別。因此,封裝 jar 或 war 時可用的所有標準組態選項,在封裝可執行 jar 或 war 時也可用。還提供許多特定於可執行 jar 和 war 的組態選項。

組態 Main Class

預設情況下,可執行歸檔文件的 main class 將會透過在主要來源集的輸出中尋找具有 public static void main(String[]) 方法的類別來自動組態。

也可以使用任務的 mainClass 屬性明確組態 main class

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	mainClass = 'com.example.ExampleApplication'
}
tasks.named<BootJar>("bootJar") {
	mainClass.set("com.example.ExampleApplication")
}

或者,可以使用 Spring Boot DSL 的 mainClass 屬性,在專案範圍內組態 main class 名稱

  • Groovy

  • Kotlin

springBoot {
	mainClass = 'com.example.ExampleApplication'
}
springBoot {
	mainClass.set("com.example.ExampleApplication")
}

如果已套用 application 外掛程式,則必須組態其 mainClass 屬性,並且可以將其用於相同的目的

  • Groovy

  • Kotlin

application {
	mainClass = 'com.example.ExampleApplication'
}
application {
	mainClass.set("com.example.ExampleApplication")
}

最後,可以在任務的 manifest 上組態 Start-Class 屬性

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	manifest {
		attributes 'Start-Class': 'com.example.ExampleApplication'
	}
}
tasks.named<BootJar>("bootJar") {
	manifest {
		attributes("Start-Class" to "com.example.ExampleApplication")
	}
}
如果 main class 是以 Kotlin 撰寫的,則應使用產生的 Java 類別的名稱。預設情況下,這是 Kotlin 類別的名稱,並加上 Kt 後綴。例如,ExampleApplication 變成 ExampleApplicationKt。如果使用 @JvmName 定義了另一個名稱,則應使用該名稱。

包含僅限開發的依賴項

預設情況下,在 developmentOnly 組態中宣告的所有依賴項都將從可執行 jar 或 war 中排除。

如果您想要在歸檔文件中包含在 developmentOnly 組態中宣告的依賴項,請組態其任務的類別路徑以包含該組態,如下列 bootWar 任務的範例所示

  • Groovy

  • Kotlin

tasks.named("bootWar") {
	classpath configurations.developmentOnly
}
tasks.named<BootWar>("bootWar") {
	classpath(configurations["developmentOnly"])
}

組態需要解壓縮的函式庫

大多數函式庫都可以在巢狀於可執行歸檔文件時直接使用,但是某些函式庫可能會遇到問題。例如,JRuby 包含其自身的巢狀 jar 支援,此支援假設 jruby-complete.jar 始終可以直接在檔案系統上使用。

為了處理任何有問題的函式庫,可以將可執行歸檔文件組態為在執行可執行歸檔文件時,將特定的巢狀 jar 解壓縮到臨時目錄。可以使用 Ant 樣式模式來識別需要解壓縮的函式庫,這些模式會比對來源 jar 檔的絕對路徑

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	requiresUnpack '**/jruby-complete-*.jar'
}
tasks.named<BootJar>("bootJar") {
	requiresUnpack("**/jruby-complete-*.jar")
}

為了獲得更多控制,也可以使用閉包。閉包會傳遞 FileTreeElement,並且應傳回 boolean 值,指示是否需要解壓縮。

使歸檔文件完全可執行

Spring Boot 提供對完全可執行歸檔文件的支援。透過在歸檔文件前面加上知道如何啟動應用程式的 shell 腳本,使歸檔文件完全可執行。在類 Unix 平台上,此啟動腳本允許直接執行歸檔文件,就像任何其他可執行文件一樣,或將其安裝為服務。

目前,某些工具不接受此格式,因此您可能不一定能夠使用此技術。例如,jar -xf 可能會靜默地無法解壓縮已完全可執行的 jar 或 war。建議您僅在打算直接執行它時才啟用此選項,而不是使用 java -jar 執行或將其部署到 Servlet 容器。

若要使用此功能,必須啟用啟動腳本的包含

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	launchScript()
}
tasks.named<BootJar>("bootJar") {
	launchScript()
}

這會將 Spring Boot 的預設啟動腳本新增至歸檔文件。預設啟動腳本包含多個具有合理預設值的屬性。可以使用 properties 屬性自訂這些值

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	launchScript {
		properties 'logFilename': 'example-app.log'
	}
}
tasks.named<BootJar>("bootJar") {
	launchScript {
		properties(mapOf("logFilename" to "example-app.log"))
	}
}

如果預設啟動腳本不符合您的需求,則可以使用 script 屬性來提供自訂啟動腳本

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	launchScript {
		script = file('src/custom.script')
	}
}
tasks.named<BootJar>("bootJar") {
	launchScript {
		script = file("src/custom.script")
	}
}

使用 PropertiesLauncher

若要使用 PropertiesLauncher 啟動可執行 jar 或 war,請組態任務的 manifest 以設定 Main-Class 屬性

  • Groovy

  • Kotlin

tasks.named("bootWar") {
	manifest {
		attributes 'Main-Class': 'org.springframework.boot.loader.launch.PropertiesLauncher'
	}
}
tasks.named<BootWar>("bootWar") {
	manifest {
		attributes("Main-Class" to "org.springframework.boot.loader.launch.PropertiesLauncher")
	}
}

封裝分層 Jar 或 War

預設情況下,bootJar 任務會建置一個歸檔文件,其中包含應用程式的類別和依賴項,分別位於 BOOT-INF/classesBOOT-INF/lib 中。同樣地,bootWar 會建置一個歸檔文件,其中包含應用程式的類別,位於 WEB-INF/classes 中,以及依賴項,位於 WEB-INF/libWEB-INF/lib-provided 中。對於需要從 jar 內容建置 Docker 映像檔的情況,能夠進一步分隔這些目錄非常有用,以便可以將它們寫入不同的層。

分層 jar 使用與常規 Boot 封裝 jar 相同的佈局,但包含一個額外的元數據檔,用於描述每個層。

預設情況下,定義了以下層

  • dependencies,適用於版本不包含 SNAPSHOT 的任何非專案依賴項。

  • spring-boot-loader,適用於 jar 加載器類別。

  • snapshot-dependencies,適用於版本包含 SNAPSHOT 的任何非專案依賴項。

  • application,適用於專案依賴項、應用程式類別和資源。

層的順序很重要,因為它決定了當應用程式的一部分變更時,先前層被快取的可能性。預設順序為 dependenciesspring-boot-loadersnapshot-dependenciesapplication。應先新增最不可能變更的內容,然後再新增更可能變更的層。

若要停用此功能,您可以透過以下方式執行

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	layered {
		enabled = false
	}
}
tasks.named<BootJar>("bootJar") {
	layered {
		enabled.set(false)
	}
}

當建立分層 jar 或 war 時,spring-boot-jarmode-tools jar 將會作為依賴項新增至您的歸檔文件。透過類別路徑上的此 jar,您可以在特殊模式下啟動您的應用程式,此模式允許引導程式碼執行與您的應用程式完全不同的操作,例如,提取層的操作。如果您希望排除此依賴項,您可以透過以下方式執行

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	includeTools = false
}
tasks.named<BootJar>("bootJar") {
	includeTools.set(false)
}

自訂層組態

根據您的應用程式,您可能想要調整層的建立方式並新增新的層。

可以使用組態來完成此操作,該組態描述如何將 jar 或 war 分隔成層,以及這些層的順序。以下範例顯示如何明確定義上述預設順序

  • Groovy

  • Kotlin

tasks.named("bootJar") {
	layered {
		application {
			intoLayer("spring-boot-loader") {
				include "org/springframework/boot/loader/**"
			}
			intoLayer("application")
		}
		dependencies {
			intoLayer("application") {
				includeProjectDependencies()
			}
			intoLayer("snapshot-dependencies") {
				include "*:*:*SNAPSHOT"
			}
			intoLayer("dependencies")
		}
		layerOrder = ["dependencies", "spring-boot-loader", "snapshot-dependencies", "application"]
	}
}
tasks.named<BootJar>("bootJar") {
	layered {
		application {
			intoLayer("spring-boot-loader") {
				include("org/springframework/boot/loader/**")
			}
			intoLayer("application")
		}
		dependencies {
			intoLayer("application") {
				includeProjectDependencies()
			}
			intoLayer("snapshot-dependencies") {
				include("*:*:*SNAPSHOT")
			}
			intoLayer("dependencies")
		}
		layerOrder.set(listOf("dependencies", "spring-boot-loader", "snapshot-dependencies", "application"))
	}
}

layered DSL 是使用三個部分定義的

  • application 閉包定義應用程式類別和資源應如何分層。

  • dependencies 閉包定義依賴項應如何分層。

  • layerOrder 方法定義應寫入層的順序。

巢狀 intoLayer 閉包用於 applicationdependencies 區段中,以宣告層的內容。這些閉包會按照定義的順序 (從上到下) 評估。任何未被先前的 intoLayer 閉包宣告的內容,仍然可供後續的閉包考慮。

intoLayer 閉包使用巢狀 includeexclude 呼叫來宣告內容。application 閉包針對 include/exclude 參數使用 Ant 樣式路徑匹配。dependencies 區段使用 group:artifact[:version] 模式。它還提供 includeProjectDependencies()excludeProjectDependencies() 方法,可用於包含或排除專案依賴項。

如果未進行 include 呼叫,則會考慮所有內容 (未被先前的閉包宣告)。

如果未進行 exclude 呼叫,則不會套用任何排除。

查看上面範例中的 dependencies 閉包,我們可以看到第一個 intoLayer 將為 application 層宣告所有專案依賴項。下一個 intoLayer 將為 snapshot-dependencies 層宣告所有 SNAPSHOT 依賴項。第三個也是最後一個 intoLayer 將為 dependencies 層宣告任何剩餘內容 (在本例中,任何不是專案依賴項或 SNAPSHOT 的依賴項)。

application 閉包具有類似的規則。首先為 spring-boot-loader 層宣告 org/springframework/boot/loader/** 內容。然後為 application 層宣告任何剩餘的類別和資源。

新增 intoLayer 閉包的順序通常與寫入層的順序不同。因此,layerOrder 方法必須始終被呼叫,並且必須涵蓋 intoLayer 呼叫引用的所有層。