打包可执行存档

该插件可以创建包含应用程序所有依赖项的可执行存档(jar 文件和 war 文件),然后可以使用 java -jar 运行。

打包可执行 Jar 文件

可以使用 bootJar 任务构建可执行 jar 文件。当应用 java 插件时,该任务会自动创建,并且是 BootJar 的实例。assemble 任务会自动配置为依赖于 bootJar 任务,因此运行 assemble(或 build)也会运行 bootJar 任务。

打包可执行 War 文件

可以使用 bootWar 任务构建可执行 war 文件。当应用 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 目录中,从那里它们不会与外部容器自己的类发生冲突。

providedRuntime 比 Gradle 的 compileOnly 配置更受欢迎,因为除了其他限制之外,compileOnly 依赖项不在测试类路径上,因此任何基于 Web 的集成测试都将失败。

打包可执行文件和普通存档

默认情况下,当配置 bootJarbootWar 任务时,jarwar 任务被配置为使用 plain 作为其存档分类器的约定。这确保了 bootJarjarbootWarwar 具有不同的输出位置,允许同时构建可执行存档和普通存档。

如果您希望可执行存档而不是普通存档使用分类器,请按照以下示例中针对 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
}
创建本机映像时,请勿禁用 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")
}

最后,可以在任务的清单中配置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值,指示是否需要解压缩。

使存档完全可执行

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/classesBOOT-INF/lib 中包含应用程序的类和依赖项。类似地,bootWar 构建一个存档,该存档在 WEB-INF/classes 中包含应用程序的类,在 WEB-INF/libWEB-INF/lib-provided 中包含依赖项。对于需要从 jar 内容构建 docker 镜像的情况,能够进一步分离这些目录非常有用,以便可以将它们写入不同的层。

分层 jar 使用与常规引导打包 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 闭包定义了如何对应用程序类和资源进行分层。

  • dependencies 闭包定义了如何对依赖项进行分层。

  • layerOrder 方法定义了层写入的顺序。

嵌套的 intoLayer 闭包用于 applicationdependencies 部分中,以声明层的內容。这些闭包按定义顺序从上到下进行评估。任何未被早期 intoLayer 闭包声明的內容都可供后续闭包考虑。

intoLayer 闭包使用嵌套的 includeexclude 调用来声明內容。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 调用引用的所有层。