打包可执行归档文件
该插件可以创建可执行的归档文件(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-webmvc')
providedRuntime('org.springframework.boot:spring-boot-starter-tomcat-runtime')
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat-runtime")
}
这确保了运行时的 JAR 文件被打包到 WAR 文件的 WEB-INF/lib-provided 目录中,从而不会与外部容器自身的类发生冲突。
providedRuntime 优于 Gradle 的 compileOnly 配置,因为 compileOnly 存在一些限制,例如 compileOnly 依赖项不会包含在测试类路径中,因此任何基于 Web 的集成测试都会失败。
打包可执行文件与普通归档文件
默认情况下,当配置 bootJar 或 bootWar 任务时,jar 或 war 任务会被配置为使用 plain 作为其归档分类器(archive classifier)的约定。这确保了 bootJar 与 jar,或 bootWar 与 war 具有不同的输出位置,从而允许可执行归档和普通归档同时构建。
如果你希望可执行归档文件(而非普通归档文件)使用一个 classifier,请按以下示例为 jar 和 bootJar 任务配置 classifiers:
- 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
}
在创建原生镜像时,不要禁用 jar 任务。详情请参见 #33238。
配置可执行归档打包
配置主类
默认情况下,可执行归档文件的主类将通过在主源集的输出中查找具有 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 plugin,则必须配置其 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 配置中声明的依赖项,请配置其任务的 classpath 以包含该配置,如下例所示(针对 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 值,以指示是否需要解包。
使用 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/classes 和 BOOT-INF/lib 中。类似地,bootWar 任务会构建一个归档文件,其中应用程序的类位于 WEB-INF/classes 中,依赖项位于 WEB-INF/lib 和 WEB-INF/lib-provided 中。在需要从 JAR 文件内容构建 Docker 镜像的情况下,能够进一步分离这些目录非常有用,这样它们就可以被写入不同的层中。
分层 JAR 使用与常规的 boot 打包 JAR 相同的布局,但包含一个额外的元数据文件,用于描述每一层。
默认情况下,定义了以下层:
-
dependencies用于任何版本号中不包含SNAPSHOT的非项目依赖。 -
spring-boot-loader用于 jar 加载器类。 -
snapshot-dependencies用于任何版本号中包含SNAPSHOT的非项目依赖。 -
application用于项目依赖、应用程序类和资源。
层的顺序很重要,因为它决定了当应用程序的一部分发生变化时,先前的层有多大可能被缓存。默认顺序为 dependencies、spring-boot-loader、snapshot-dependencies、application。最不可能发生变化的内容应最先添加,随后是更有可能发生变化的层。
要禁用此功能,您可以按以下方式操作:
- Groovy
- Kotlin
tasks.named("bootJar") {
layered {
enabled = false
}
}
tasks.named<BootJar>("bootJar") {
layered {
enabled.set(false)
}
}
当创建分层的 jar 或 war 时,spring-boot-jarmode-tools jar 将作为依赖项添加到你的归档文件中。当该 jar 位于 classpath 上时,你可以以一种特殊模式启动应用程序,该模式允许引导代码运行与你的应用程序完全不同的内容,例如提取各层的内容。如果你希望排除此依赖项,可以按以下方式操作:
- 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 闭包用于 application 和 dependencies 部分中,以声明某一层的内容。这些闭包按照定义的顺序从上到下依次求值。任何未被前面 intoLayer 闭包声明的内容,将保持可用状态,供后续闭包考虑使用。
intoLayer 闭包通过嵌套的 include 和 exclude 调用来声明内容。application 闭包对 include/exclude 参数使用 Ant 风格的路径匹配。dependencies 部分使用 group:artifact[:version] 模式。它还提供了 includeProjectDependencies() 和 excludeProjectDependencies() 方法,可用于包含或排除项目依赖项。
如果没有调用 include,则所有内容(未被之前的闭包声明的部分)都会被考虑。
如果没有调用 exclude,则不会应用任何排除规则。
查看上面示例中的 dependencies 闭包,我们可以看到第一个 intoLayer 会将所有项目依赖分配给 application 层。下一个 intoLayer 会将所有 SNAPSHOT 依赖分配给 snapshot-dependencies 层。第三个也是最后一个 intoLayer 会将剩余的所有内容(在本例中,即既不是项目依赖也不是 SNAPSHOT 的依赖)分配给 dependencies 层。
application 闭包具有类似的规则。首先将 org/springframework/boot/loader/** 内容分配给 spring-boot-loader 层,然后将剩余的类和资源分配给 application 层。
intoLayer 闭包的添加顺序通常与图层的书写顺序不同。因此,必须始终调用 layerOrder 方法,并且该方法 必须 覆盖所有 intoLayer 调用中引用的图层。