打包可执行归档文件
该插件可以创建可执行归档文件(jar 文件和 war 文件),这些文件包含应用程序的所有依赖项,然后可以使用 java -jar
运行。
打包可执行 Jars
可以使用 bootJar
任务构建可执行 jar。
当应用 java
插件时,该任务会自动创建,并且是 BootJar
的一个实例。
assemble
任务会自动配置为依赖于 bootJar
任务,因此运行 assemble
(或 build
)也会运行 bootJar
任务。
打包可执行 Wars
可以使用 bootWar
任务构建可执行 war。
当应用 war
插件时,该任务会自动创建,并且是 BootWar
的一个实例。
assemble
任务会自动配置为依赖于 bootWar
任务,因此运行 assemble
(或 build
)也会运行 bootWar
任务。
打包可执行和可部署的 Wars
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
目录中,这样它们就不会与外部容器自己的类冲突。
注意: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 类的名称。
默认情况下,这是添加了 Kt
后缀的 Kotlin 类的名称。
例如,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,请配置任务的清单以设置 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
方法定义层的写入顺序。
在 application
和 dependencies
部分中使用嵌套的 intoLayer
闭包来为层声明内容。
这些闭包按定义的顺序从上到下进行评估。
任何未被较早的 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
调用引用的所有层。