可执行归档包打包
该插件可以创建包含应用所有依赖项的可执行归档包(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 的集成测试失败。
|
可执行归档包与普通归档包同时打包
默认情况下,当配置了 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
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
配置声明的依赖包含进归档包,可按如下示例为 bootWar
任务配置其 classpath:
-
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/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
方法定义各层写入顺序
在 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 引用的层。
|