打包可执行归档
该插件可以创建包含应用程序所有依赖的可执行档案(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 依赖项不在测试类路径上,因此任何基于 Web 的集成测试都将失败。 |
打包可执行和普通档案
默认情况下,当配置 bootJar 或 bootWar 任务时,jar 或 war 任务会配置为使用 plain 作为其档案分类器的约定。这确保了 bootJar 和 jar 或 bootWar 和 war 具有不同的输出位置,允许同时构建可执行档案和普通档案。
如果你更喜欢可执行档案(而不是普通档案)使用分类器,请按照以下示例为 jar 和 bootJar 任务配置分类器
-
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。 |
配置可执行档案打包
BootJar 和 BootWar 任务分别是 Gradle 的 Jar 和 War 任务的子类。因此,打包 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")
}
最后,可以在任务的清单中配置 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")
}
为了获得更多控制权,还可以使用闭包。闭包会传递一个 FileTreeElement,并且应该返回一个 boolean 值,指示是否需要解压。
使用 PropertiesLauncher
要使用 PropertiesLauncher 启动可执行 jar 或 war,请配置任务的清单以设置 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 在类路径上,你可以以一种特殊模式启动应用程序,该模式允许引导代码运行与应用程序完全不同的东西,例如,提取层的代码。如果你希望排除此依赖项,可以按以下方式操作
-
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 闭包使用 Ant 风格的路径匹配作为 include/exclude 参数。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 调用引用的所有层。 |