打包可執行檔案

該外掛可以建立可執行檔案(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 作為其檔案分類器的約定。這確保 bootJarjar(或 bootWarwar)具有不同的輸出位置,允許同時構建可執行檔案和普通檔案。

如果你偏好可執行檔案使用分類器而非普通檔案使用,請按照以下示例為 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 Image 時不要停用 jar 任務。詳見 #33238

配置可執行檔案打包

BootJarBootWar 任務分別是 Gradle 的 JarWar 任務的子類。因此,打包 jar 或 war 時所有可用的標準配置選項在打包可執行 jar 或 war 時也可用。此外,還提供了一些針對可執行 jar 和 war 的特定配置選項。

配置主類

預設情況下,可執行檔案的主類將透過在主源集的輸出中查詢具有 public static void main(String[]) 方法的類來自動配置。

也可以使用任務的 mainClass 屬性顯式配置主類

  • Groovy

  • Kotlin

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

或者,可以使用 Spring Boot DSL 的 mainClass 屬性在專案範圍內配置主類名稱

  • 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")
	}
}
如果主類是用 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")
}

為了獲得更多控制,還可以使用 closure。closure 會被傳入一個 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 closure 定義了應用程式類和資源如何分層。

  • dependencies closure 定義了依賴項如何分層。

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

巢狀的 intoLayer closures 用於 applicationdependencies 部分,以宣告某個層的內容。這些 closures 按其定義的順序從上到下進行評估。任何未被早期 intoLayer closure 宣告的內容對後續 closure 仍然可用。

intoLayer closure 使用巢狀的 includeexclude 呼叫宣告內容。application closure 使用 Ant 風格路徑匹配作為 include/exclude 引數。dependencies 部分使用 group:artifact[:version] 模式。它還提供了 includeProjectDependencies()excludeProjectDependencies() 方法,可用於包含或排除專案依賴項。

如果沒有 include 呼叫,則考慮所有內容(未被早期 closure 宣告的內容)。

如果沒有 exclude 呼叫,則不應用任何排除。

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

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

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