打包可执行归档文件

该插件可以创建包含应用程序所有依赖项的可执行归档文件(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解压到临时目录。可以使用与源jar文件绝对路径匹配的Ant风格模式来识别需要解压的库

  • 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相同的布局,但包含一个描述每个层的附加元数据文件。

默认情况下,定义以下层

  • 版本不包含SNAPSHOT的任何非项目依赖项的dependencies

  • jar加载器类的spring-boot-loader

  • 版本包含SNAPSHOT的任何非项目依赖项的snapshot-dependencies

  • 项目依赖项、应用程序类和资源的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闭包对include/exclude参数使用Ant风格的路径匹配。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 调用引用的所有层。