1. Introduction
The Spring Boot Gradle Plugin provides Spring Boot support in Gradle.
It allows you to package executable jar or war archives, run Spring Boot applications, and use the dependency management provided by spring-boot-dependencies
.
Spring Boot’s Gradle plugin requires Gradle 7.x (7.5 or later) or 8.x and can be used with Gradle’s configuration cache.
In addition to this user guide, API documentation is also available.
2. Getting Started
To get started with the plugin it needs to be applied to your project.
The plugin is published to the Spring Commercial repository. You will have to configure your build to access this repository. This is usual done through a local artifact repository that mirrors the content of the Spring Commercial repository. Alternatively, while it is not recommended, the Spring Commercial repository can also be accessed directly. In either case, see the Tanzu Spring Runtime documentation for further details.
With access to the Spring Commercial repository configured in settings.gradle
or settings.gradle.kts
, the plugin can be applied using the plugins
block:
plugins {
id 'org.springframework.boot' version '3.0.17.4'
}
plugins {
id("org.springframework.boot") version "3.0.17.4"
}
Applied in isolation the plugin makes few changes to a project.
Instead, the plugin detects when certain other plugins are applied and reacts accordingly.
For example, when the java
plugin is applied a task for building an executable jar is automatically configured.
A typical Spring Boot project will apply the groovy
, java
, or org.jetbrains.kotlin.jvm
plugin as a minimum and also use the io.spring.dependency-management
plugin or Gradle’s native bom support for dependency management.
For example:
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.17.4'
}
apply plugin: 'io.spring.dependency-management'
plugins {
java
id("org.springframework.boot") version "3.0.17.4"
}
apply(plugin = "io.spring.dependency-management")
To learn more about how the Spring Boot plugin behaves when other plugins are applied please see the section on reacting to other plugins.
3. Managing Dependencies
To manage dependencies in your Spring Boot application, you can either apply the io.spring.dependency-management
plugin or use Gradle’s native bom support.
The primary benefit of the former is that it offers property-based customization of managed versions, while using the latter will likely result in faster builds.
3.1. Managing Dependencies with the Dependency Management Plugin
When you apply the io.spring.dependency-management
plugin, Spring Boot’s plugin will automatically import the spring-boot-dependencies
bom from the version of Spring Boot that you are using.
This provides a similar dependency management experience to the one that’s enjoyed by Maven users.
For example, it allows you to omit version numbers when declaring dependencies that are managed in the bom.
To make use of this functionality, declare dependencies in the usual way but omit the version number:
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
}
3.1.1. Customizing Managed Versions
The spring-boot-dependencies
bom that is automatically imported when the dependency management plugin is applied uses properties to control the versions of the dependencies that it manages.
Browse the Dependency versions Appendix
in the Spring Boot reference for a complete list of these properties.
To customize a managed version you set its corresponding property.
For example, to customize the version of SLF4J which is controlled by the slf4j.version
property:
ext['slf4j.version'] = '1.7.20'
extra["slf4j.version"] = "1.7.20"
Each Spring Boot release is designed and tested against a specific set of third-party dependencies. Overriding versions may cause compatibility issues and should be done with care. |
3.1.2. Using Spring Boot’s Dependency Management in Isolation
Spring Boot’s dependency management can be used in a project without applying Spring Boot’s plugin to that project.
The SpringBootPlugin
class provides a BOM_COORDINATES
constant that can be used to import the bom without having to know its group ID, artifact ID, or version.
First, configure the project to depend on the Spring Boot plugin but do not apply it:
plugins {
id 'org.springframework.boot' version '3.0.17.4' apply false
}
plugins {
id("org.springframework.boot") version "3.0.17.4" apply false
}
The Spring Boot plugin’s dependency on the dependency management plugin means that you can use the dependency management plugin without having to declare a dependency on it. This also means that you will automatically use the same version of the dependency management plugin as Spring Boot uses.
Apply the dependency management plugin and then configure it to import Spring Boot’s bom:
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
}
}
apply(plugin = "io.spring.dependency-management")
the<DependencyManagementExtension>().apply {
imports {
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
}
The Kotlin code above is a bit awkward. That’s because we’re using the imperative way of applying the dependency management plugin.
We can make the code less awkward by applying the plugin from the root parent project, or by using the plugins
block as we’re doing for the Spring Boot plugin.
A downside of this method is that it forces us to specify the version of the dependency management plugin:
plugins {
java
id("org.springframework.boot") version "3.0.17.4" apply false
id("io.spring.dependency-management") version "1.1.6"
}
dependencyManagement {
imports {
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
}
3.1.3. Learning More
To learn more about the capabilities of the dependency management plugin, please refer to its documentation.
3.2. Managing Dependencies with Gradle’s Bom Support
Gradle allows a bom to be used to manage a project’s versions by declaring it as a platform
or enforcedPlatform
dependency.
A platform
dependency treats the versions in the bom as recommendations and other versions and constraints in the dependency graph may cause a version of a dependency other than that declared in the bom to be used.
An enforcedPlatform
dependency treats the versions in the bom as requirements and they will override any other version found in the dependency graph.
The SpringBootPlugin
class provides a BOM_COORDINATES
constant that can be used to declare a dependency upon Spring Boot’s bom without having to know its group ID, artifact ID, or version, as shown in the following example:
dependencies {
implementation platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
}
dependencies {
implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
}
A platform or enforced platform will only constrain the versions of the configuration in which it has been declared or that extend from the configuration in which it has been declared. As a result, in may be necessary to declare the same dependency in more than one configuration.
3.2.1. Customizing Managed Versions
When using Gradle’s bom support, you cannot use the properties from spring-boot-dependencies
to control the versions of the dependencies that it manages.
Instead, you must use one of the mechanisms that Gradle provides.
One such mechanism is a resolution strategy.
SLF4J’s modules are all in the org.slf4j
group so their version can be controlled by configuring every dependency in that group to use a particular version, as shown in the following example:
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'org.slf4j') {
details.useVersion '1.7.20'
}
}
}
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "org.slf4j") {
useVersion("1.7.20")
}
}
}
Each Spring Boot release is designed and tested against a specific set of third-party dependencies. Overriding versions may cause compatibility issues and should be done with care. |
4. Packaging Executable Archives
The plugin can create executable archives (jar files and war files) that contain all of an application’s dependencies and can then be run with java -jar
.
4.1. Packaging Executable Jars
Executable jars can be built using the bootJar
task.
The task is automatically created when the java
plugin is applied and is an instance of BootJar
.
The assemble
task is automatically configured to depend upon the bootJar
task so running assemble
(or build
) will also run the bootJar
task.
4.2. Packaging Executable Wars
Executable wars can be built using the bootWar
task.
The task is automatically created when the war
plugin is applied and is an instance of BootWar
.
The assemble
task is automatically configured to depend upon the bootWar
task so running assemble
(or build
) will also run the bootWar
task.
4.2.1. Packaging Executable and Deployable Wars
A war file can be packaged such that it can be executed using java -jar
and deployed to an external container.
To do so, the embedded servlet container dependencies should be added to the providedRuntime
configuration, for example:
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")
}
This ensures that they are package in the war file’s WEB-INF/lib-provided
directory from where they will not conflict with the external container’s own classes.
providedRuntime is preferred to Gradle’s compileOnly configuration as, among other limitations, compileOnly dependencies are not on the test classpath so any web-based integration tests will fail.
|
4.3. Packaging Executable and Plain Archives
By default, when the bootJar
or bootWar
tasks are configured, the jar
or war
tasks are configured to use plain
as the convention for their archive classifier.
This ensures that bootJar
and jar
or bootWar
and war
have different output locations, allowing both the executable archive and the plain archive to be built at the same time.
If you prefer that the executable archive, rather than the plain archive, uses a classifier, configure the classifiers as shown in the following example for the jar
and bootJar
tasks:
tasks.named("bootJar") {
archiveClassifier = 'boot'
}
tasks.named("jar") {
archiveClassifier = ''
}
tasks.named<BootJar>("bootJar") {
archiveClassifier.set("boot")
}
tasks.named<Jar>("jar") {
archiveClassifier.set("")
}
Alternatively, if you prefer that the plain archive isn’t built at all, disable its task as shown in the following example for the jar
task:
tasks.named("jar") {
enabled = false
}
tasks.named<Jar>("jar") {
enabled = false
}
Do not disable the jar task when creating native images.
See #33238 for details.
|
4.4. Configuring Executable Archive Packaging
The BootJar
and BootWar
tasks are subclasses of Gradle’s Jar
and War
tasks respectively.
As a result, all of the standard configuration options that are available when packaging a jar or war are also available when packaging an executable jar or war.
A number of configuration options that are specific to executable jars and wars are also provided.
4.4.1. Configuring the Main Class
By default, the executable archive’s main class will be configured automatically by looking for a class with a public static void main(String[])
method in the main source set’s output.
The main class can also be configured explicitly using the task’s mainClass
property:
tasks.named("bootJar") {
mainClass = 'com.example.ExampleApplication'
}
tasks.named<BootJar>("bootJar") {
mainClass.set("com.example.ExampleApplication")
}
Alternatively, the main class name can be configured project-wide using the mainClass
property of the Spring Boot DSL:
springBoot {
mainClass = 'com.example.ExampleApplication'
}
springBoot {
mainClass.set("com.example.ExampleApplication")
}
If the application
plugin has been applied its mainClass
property must be configured and can be used for the same purpose:
application {
mainClass = 'com.example.ExampleApplication'
}
application {
mainClass.set("com.example.ExampleApplication")
}
Lastly, the Start-Class
attribute can be configured on the task’s manifest:
tasks.named("bootJar") {
manifest {
attributes 'Start-Class': 'com.example.ExampleApplication'
}
}
tasks.named<BootJar>("bootJar") {
manifest {
attributes("Start-Class" to "com.example.ExampleApplication")
}
}
If the main class is written in Kotlin, the name of the generated Java class should be used.
By default, this is the name of the Kotlin class with the Kt suffix added.
For example, ExampleApplication becomes ExampleApplicationKt .
If another name is defined using @JvmName then that name should be used.
|
4.4.2. Including Development-only Dependencies
By default all dependencies declared in the developmentOnly
configuration will be excluded from an executable jar or war.
If you want to include dependencies declared in the developmentOnly
configuration in your archive, configure the classpath of its task to include the configuration, as shown in the following example for the bootWar
task:
tasks.named("bootWar") {
classpath configurations.developmentOnly
}
tasks.named<BootWar>("bootWar") {
classpath(configurations["developmentOnly"])
}
4.4.3. Configuring Libraries that Require Unpacking
Most libraries can be used directly when nested in an executable archive, however certain libraries can have problems.
For example, JRuby includes its own nested jar support which assumes that jruby-complete.jar
is always directly available on the file system.
To deal with any problematic libraries, an executable archive can be configured to unpack specific nested jars to a temporary directory when the executable archive is run. Libraries can be identified as requiring unpacking using Ant-style patterns that match against the absolute path of the source jar file:
tasks.named("bootJar") {
requiresUnpack '**/jruby-complete-*.jar'
}
tasks.named<BootJar>("bootJar") {
requiresUnpack("**/jruby-complete-*.jar")
}
For more control a closure can also be used.
The closure is passed a FileTreeElement
and should return a boolean
indicating whether or not unpacking is required.
4.4.4. Making an Archive Fully Executable
Spring Boot provides support for fully executable archives. An archive is made fully executable by prepending a shell script that knows how to launch the application. On Unix-like platforms, this launch script allows the archive to be run directly like any other executable or to be installed as a service.
Currently, some tools do not accept this format so you may not always be able to use this technique.
For example, jar -xf may silently fail to extract a jar or war that has been made fully-executable.
It is recommended that you only enable this option if you intend to execute it directly, rather than running it with java -jar or deploying it to a servlet container.
|
To use this feature, the inclusion of the launch script must be enabled:
tasks.named("bootJar") {
launchScript()
}
tasks.named<BootJar>("bootJar") {
launchScript()
}
This will add Spring Boot’s default launch script to the archive.
The default launch script includes several properties with sensible default values.
The values can be customized using the properties
property:
tasks.named("bootJar") {
launchScript {
properties 'logFilename': 'example-app.log'
}
}
tasks.named<BootJar>("bootJar") {
launchScript {
properties(mapOf("logFilename" to "example-app.log"))
}
}
If the default launch script does not meet your needs, the script
property can be used to provide a custom launch script:
tasks.named("bootJar") {
launchScript {
script = file('src/custom.script')
}
}
tasks.named<BootJar>("bootJar") {
launchScript {
script = file("src/custom.script")
}
}
4.4.5. Using the PropertiesLauncher
To use the PropertiesLauncher
to launch an executable jar or war, configure the task’s manifest to set the Main-Class
attribute:
tasks.named("bootWar") {
manifest {
attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher'
}
}
tasks.named<BootWar>("bootWar") {
manifest {
attributes("Main-Class" to "org.springframework.boot.loader.PropertiesLauncher")
}
}
4.4.6. Packaging Layered Jar or War
By default, the bootJar
task builds an archive that contains the application’s classes and dependencies in BOOT-INF/classes
and BOOT-INF/lib
respectively.
Similarly, bootWar
builds an archive that contains the application’s classes in WEB-INF/classes
and dependencies in WEB-INF/lib
and WEB-INF/lib-provided
.
For cases where a docker image needs to be built from the contents of the jar, it’s useful to be able to separate these directories further so that they can be written into distinct layers.
Layered jars use the same layout as regular boot packaged jars, but include an additional meta-data file that describes each layer.
By default, the following layers are defined:
-
dependencies
for any non-project dependency whose version does not containSNAPSHOT
. -
spring-boot-loader
for the jar loader classes. -
snapshot-dependencies
for any non-project dependency whose version containsSNAPSHOT
. -
application
for project dependencies, application classes, and resources.
The layers order is important as it determines how likely previous layers can be cached when part of the application changes.
The default order is dependencies
, spring-boot-loader
, snapshot-dependencies
, application
.
Content that is least likely to change should be added first, followed by layers that are more likely to change.
To disable this feature, you can do so in the following manner:
tasks.named("bootJar") {
layered {
enabled = false
}
}
tasks.named<BootJar>("bootJar") {
layered {
enabled.set(false)
}
}
When a layered jar or war is created, the spring-boot-jarmode-layertools
jar will be added as a dependency to your archive.
With this jar on the classpath, you can launch your application in a special mode which allows the bootstrap code to run something entirely different from your application, for example, something that extracts the layers.
If you wish to exclude this dependency, you can do so in the following manner:
tasks.named("bootJar") {
layered {
includeLayerTools = false
}
}
tasks.named<BootJar>("bootJar") {
layered {
includeLayerTools.set(false)
}
}
Custom Layers Configuration
Depending on your application, you may want to tune how layers are created and add new ones.
This can be done using configuration that describes how the jar or war can be separated into layers, and the order of those layers. The following example shows how the default ordering described above can be defined explicitly:
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"))
}
}
The layered
DSL is defined using three parts:
-
The
application
closure defines how the application classes and resources should be layered. -
The
dependencies
closure defines how dependencies should be layered. -
The
layerOrder
method defines the order that the layers should be written.
Nested intoLayer
closures are used within application
and dependencies
sections to claim content for a layer.
These closures are evaluated in the order that they are defined, from top to bottom.
Any content not claimed by an earlier intoLayer
closure remains available for subsequent ones to consider.
The intoLayer
closure claims content using nested include
and exclude
calls.
The application
closure uses Ant-style path matching for include/exclude parameters.
The dependencies
section uses group:artifact[:version]
patterns.
It also provides includeProjectDependencies()
and excludeProjectDependencies()
methods that can be used to include or exclude project dependencies.
If no include
call is made, then all content (not claimed by an earlier closure) is considered.
If no exclude
call is made, then no exclusions are applied.
Looking at the dependencies
closure in the example above, we can see that the first intoLayer
will claim all project dependencies for the application
layer.
The next intoLayer
will claim all SNAPSHOT dependencies for the snapshot-dependencies
layer.
The third and final intoLayer
will claim anything left (in this case, any dependency that is not a project dependency or a SNAPSHOT) for the dependencies
layer.
The application
closure has similar rules.
First claiming org/springframework/boot/loader/**
content for the spring-boot-loader
layer.
Then claiming any remaining classes and resources for the application
layer.
The order that intoLayer closures are added is often different from the order that the layers are written.
For this reason the layerOrder method must always be called and must cover all layers referenced by the intoLayer calls.
|
5. Packaging OCI Images
The plugin can create an OCI image from a jar or war file using Cloud Native Buildpacks (CNB).
Images can be built using the bootBuildImage
task.
For security reasons, images build and run as non-root users. See the CNB specification for more details. |
The task is automatically created when the java
or war
plugin is applied and is an instance of BootBuildImage
.
5.1. Docker Daemon
The bootBuildImage
task requires access to a Docker daemon.
By default, it will communicate with a Docker daemon over a local connection.
This works with Docker Engine on all supported platforms without configuration.
Environment variables can be set to configure the bootBuildImage
task to use an alternative local or remote connection.
The following table shows the environment variables and their values:
Environment variable | Description |
---|---|
DOCKER_HOST |
URL containing the host and port for the Docker daemon - for example |
DOCKER_TLS_VERIFY |
Enable secure HTTPS protocol when set to |
DOCKER_CERT_PATH |
Path to certificate and key files for HTTPS (required if |
Docker daemon connection information can also be provided using docker
properties in the plugin configuration.
The following table summarizes the available properties:
Property | Description |
---|---|
|
URL containing the host and port for the Docker daemon - for example |
|
Enable secure HTTPS protocol when set to |
|
Path to certificate and key files for HTTPS (required if |
|
When |
For more details, see also examples.
5.2. Docker Registry
If the Docker images specified by the builder
or runImage
properties are stored in a private Docker image registry that requires authentication, the authentication credentials can be provided using docker.builderRegistry
properties.
If the generated Docker image is to be published to a Docker image registry, the authentication credentials can be provided using docker.publishRegistry
properties.
Properties are provided for user authentication or identity token authentication. Consult the documentation for the Docker registry being used to store images for further information on supported authentication methods.
The following table summarizes the available properties for docker.builderRegistry
and docker.publishRegistry
:
Property | Description |
---|---|
|
Username for the Docker image registry user. Required for user authentication. |
|
Password for the Docker image registry user. Required for user authentication. |
|
Address of the Docker image registry. Optional for user authentication. |
|
E-mail address for the Docker image registry user. Optional for user authentication. |
|
Identity token for the Docker image registry user. Required for token authentication. |
For more details, see also examples.
5.3. Image Customizations
The plugin invokes a builder to orchestrate the generation of an image. The builder includes multiple buildpacks that can inspect the application to influence the generated image. By default, the plugin chooses a builder image. The name of the generated image is deduced from project properties.
Task properties can be used to configure how the builder should operate on the project. The following table summarizes the available properties and their default values:
Property | Command-line option | Description | Default value |
---|---|---|---|
|
|
Name of the Builder image to use. |
|
|
|
Name of the run image to use. |
No default value, indicating the run image specified in Builder metadata should be used. |
|
|
Image name for the generated image. |
|
|
|
Policy used to determine when to pull the builder and run images from the registry.
Acceptable values are |
|
|
Environment variables that should be passed to the builder. |
Empty or |
|
|
Buildpacks that the builder should use when building the image. Only the specified buildpacks will be used, overriding the default buildpacks included in the builder. Buildpack references must be in one of the following forms:
|
None, indicating the builder should use the buildpacks included in it. |
|
|
Volume bind mounts that should be mounted to the builder container when building the image. The bindings will be passed unparsed and unvalidated to Docker when creating the builder container. Bindings must be in one of the following forms:
Where
|
||
|
|
The network driver the builder container will be configured to use. The value supplied will be passed unvalidated to Docker when creating the builder container. |
|
|
|
Whether to clean the cache before building. |
|
|
Enables verbose logging of builder operations. |
|
|
|
|
Whether to publish the generated image to a Docker registry. |
|
|
A list of one or more additional tags to apply to the generated image.
The values provided to the |
||
|
A cache containing layers created by buildpacks and used by the image building process. |
A named volume in the Docker daemon, with a name derived from the image name. |
|
|
A cache containing layers created by buildpacks and used by the image launching process. |
A named volume in the Docker daemon, with a name derived from the image name. |
The plugin detects the target Java compatibility of the project using the JavaPlugin’s targetCompatibility property.
When using the default Paketo builder and buildpacks, the plugin instructs the buildpacks to install the same Java version.
You can override this behaviour as shown in the builder configuration examples.
|
5.4. Examples
5.4.1. Custom Image Builder and Run Image
If you need to customize the builder used to create the image or the run image used to launch the built image, configure the task as shown in the following example:
tasks.named("bootBuildImage") {
builder = "mine/java-cnb-builder"
runImage = "mine/java-cnb-run"
}
tasks.named<BootBuildImage>("bootBuildImage") {
builder.set("mine/java-cnb-builder")
runImage.set("mine/java-cnb-run")
}
This configuration will use a builder image with the name mine/java-cnb-builder
and the tag latest
, and the run image named mine/java-cnb-run
and the tag latest
.
The builder and run image can be specified on the command line as well, as shown in this example:
$ gradle bootBuildImage --builder=mine/java-cnb-builder --runImage=mine/java-cnb-run
5.4.2. Builder Configuration
If the builder exposes configuration options, those can be set using the environment
property.
The following is an example of configuring the JVM version used by the Paketo Java buildpacks at build time:
tasks.named("bootBuildImage") {
environment["BP_JVM_VERSION"] = "17"
}
tasks.named<BootBuildImage>("bootBuildImage") {
environment.set(environment.get() + mapOf("BP_JVM_VERSION" to "17"))
}
If there is a network proxy between the Docker daemon the builder runs in and network locations that buildpacks download artifacts from, you will need to configure the builder to use the proxy.
When using the Paketo builder, this can be accomplished by setting the HTTPS_PROXY
and/or HTTP_PROXY
environment variables as show in the following example:
tasks.named("bootBuildImage") {
environment["HTTP_PROXY"] = "http://proxy.example.com"
environment["HTTPS_PROXY"] = "https://proxy.example.com"
}
tasks.named<BootBuildImage>("bootBuildImage") {
environment.set(mapOf("HTTP_PROXY" to "http://proxy.example.com",
"HTTPS_PROXY" to "https://proxy.example.com"))
}
5.4.3. Runtime JVM Configuration
Paketo Java buildpacks configure the JVM runtime environment by setting the JAVA_TOOL_OPTIONS
environment variable.
The buildpack-provided JAVA_TOOL_OPTIONS
value can be modified to customize JVM runtime behavior when the application image is launched in a container.
Environment variable modifications that should be stored in the image and applied to every deployment can be set as described in the Paketo documentation and shown in the following example:
tasks.named("bootBuildImage") {
environment["BPE_DELIM_JAVA_TOOL_OPTIONS"] = " "
environment["BPE_APPEND_JAVA_TOOL_OPTIONS"] = "-XX:+HeapDumpOnOutOfMemoryError"
}
tasks.named<BootBuildImage>("bootBuildImage") {
environment.set(mapOf(
"BPE_DELIM_JAVA_TOOL_OPTIONS" to " ",
"BPE_APPEND_JAVA_TOOL_OPTIONS" to "-XX:+HeapDumpOnOutOfMemoryError"
))
}
5.4.4. Custom Image Name
By default, the image name is inferred from the name
and the version
of the project, something like docker.io/library/${project.name}:${project.version}
.
You can take control over the name by setting task properties, as shown in the following example:
tasks.named("bootBuildImage") {
imageName = "example.com/library/${project.name}"
}
tasks.named<BootBuildImage>("bootBuildImage") {
imageName.set("example.com/library/${project.name}")
}
Note that this configuration does not provide an explicit tag so latest
is used.
It is possible to specify a tag as well, either using ${project.version}
, any property available in the build or a hardcoded version.
The image name can be specified on the command line as well, as shown in this example:
$ gradle bootBuildImage --imageName=example.com/library/my-app:v1
5.4.5. Buildpacks
By default, the builder will use buildpacks included in the builder image and apply them in a pre-defined order. An alternative set of buildpacks can be provided to apply buildpacks that are not included in the builder, or to change the order of included buildpacks. When one or more buildpacks are provided, only the specified buildpacks will be applied.
The following example instructs the builder to use a custom buildpack packaged in a .tgz
file, followed by a buildpack included in the builder.
tasks.named("bootBuildImage") {
buildpacks = ["file:///path/to/example-buildpack.tgz", "urn:cnb:builder:paketo-buildpacks/java"]
}
tasks.named<BootBuildImage>("bootBuildImage") {
buildpacks.set(listOf("file:///path/to/example-buildpack.tgz", "urn:cnb:builder:paketo-buildpacks/java"))
}
Buildpacks can be specified in any of the forms shown below.
A buildpack located in a CNB Builder (version may be omitted if there is only one buildpack in the builder matching the buildpack-id
):
-
urn:cnb:builder:buildpack-id
-
urn:cnb:builder:[email protected]
-
buildpack-id
A path to a directory containing buildpack content (not supported on Windows):
-
file:///path/to/buildpack/
-
/path/to/buildpack/
A path to a gzipped tar file containing buildpack content:
-
file:///path/to/buildpack.tgz
-
/path/to/buildpack.tgz
An OCI image containing a packaged buildpack:
-
docker://example/buildpack
-
docker:///example/buildpack:latest
-
docker:///example/buildpack@sha256:45b23dee08…
-
example/buildpack
-
example/buildpack:latest
-
example/buildpack@sha256:45b23dee08…
5.4.6. Image Publishing
The generated image can be published to a Docker registry by enabling a publish
option.
If the Docker registry requires authentication, the credentials can be configured using docker.publishRegistry
properties.
If the Docker registry does not require authentication, the docker.publishRegistry
configuration can be omitted.
The registry that the image will be published to is determined by the registry part of the image name (docker.example.com in these examples).
If docker.publishRegistry credentials are configured and include a url property, this value is passed to the registry but is not used to determine the publishing registry location.
|
tasks.named("bootBuildImage") {
imageName.set("docker.example.com/library/${project.name}")
publish = true
docker {
publishRegistry {
username = "user"
password = "secret"
}
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
imageName.set("docker.example.com/library/${project.name}")
publish.set(true)
docker {
publishRegistry {
username.set("user")
password.set("secret")
}
}
}
The publish option can be specified on the command line as well, as shown in this example:
$ gradle bootBuildImage --imageName=docker.example.com/library/my-app:v1 --publishImage
5.4.7. Builder Cache Configuration
The CNB builder caches layers that are used when building and launching an image. By default, these caches are stored as named volumes in the Docker daemon with names that are derived from the full name of the target image. If the image name changes frequently, for example when the project version is used as a tag in the image name, then the caches can be invalidated frequently.
The cache volumes can be configured to use alternative names to give more control over cache lifecycle as shown in the following example:
tasks.named("bootBuildImage") {
buildCache {
volume {
name = "cache-${rootProject.name}.build"
}
}
launchCache {
volume {
name = "cache-${rootProject.name}.launch"
}
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
buildCache {
volume {
name.set("cache-${rootProject.name}.build")
}
}
launchCache {
volume {
name.set("cache-${rootProject.name}.launch")
}
}
}
5.4.8. Docker Configuration
Docker Configuration for minikube
The plugin can communicate with the Docker daemon provided by minikube instead of the default local connection.
On Linux and macOS, environment variables can be set using the command eval $(minikube docker-env)
after minikube has been started.
The plugin can also be configured to use the minikube daemon by providing connection details similar to those shown in the following example:
tasks.named("bootBuildImage") {
docker {
host = "tcp://192.168.99.100:2376"
tlsVerify = true
certPath = "/home/user/.minikube/certs"
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
docker {
host.set("tcp://192.168.99.100:2376")
tlsVerify.set(true)
certPath.set("/home/user/.minikube/certs")
}
}
Docker Configuration for podman
The plugin can communicate with a podman container engine.
The plugin can be configured to use podman local connection by providing connection details similar to those shown in the following example:
tasks.named("bootBuildImage") {
docker {
host = "unix:///run/user/1000/podman/podman.sock"
bindHostToBuilder = true
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
docker {
host.set("unix:///run/user/1000/podman/podman.sock")
bindHostToBuilder.set(true)
}
}
With the podman CLI installed, the command podman info --format='{{.Host.RemoteSocket.Path}}' can be used to get the value for the docker.host configuration property shown in this example.
|
Docker Configuration for Colima
The plugin can communicate with the Docker daemon provided by Colima.
The DOCKER_HOST
environment variable can be set by using the command export DOCKER_HOST=$(docker context inspect colima -f '{{.Endpoints.docker.Host}}').
The plugin can also be configured to use Colima daemon by providing connection details similar to those shown in the following example:
tasks.named("bootBuildImage") {
docker {
host = "unix://${System.properties['user.home']}/.colima/docker.sock"
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
docker {
host.set("unix://${System.getProperty("user.home")}/.colima/docker.sock")
}
}
Docker Configuration for Authentication
If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided using docker.builderRegistry
properties as shown in the following example:
tasks.named("bootBuildImage") {
docker {
builderRegistry {
username = "user"
password = "secret"
url = "https://docker.example.com/v1/"
email = "[email protected]"
}
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
docker {
builderRegistry {
username.set("user")
password.set("secret")
url.set("https://docker.example.com/v1/")
email.set("[email protected]")
}
}
}
If the builder or run image is stored in a private Docker registry that supports token authentication, the token value can be provided using docker.builderRegistry
as shown in the following example:
tasks.named("bootBuildImage") {
docker {
builderRegistry {
token = "9cbaf023786cd7..."
}
}
}
tasks.named<BootBuildImage>("bootBuildImage") {
docker {
builderRegistry {
token.set("9cbaf023786cd7...")
}
}
}
6. Publishing your Application
6.1. Publishing with the Maven-publish Plugin
To publish your Spring Boot jar or war, add it to the publication using the artifact
method on MavenPublication
.
Pass the task that produces that artifact that you wish to publish to the artifact
method.
For example, to publish the artifact produced by the default bootJar
task:
publishing {
publications {
bootJava(MavenPublication) {
artifact tasks.named("bootJar")
}
}
repositories {
maven {
url 'https://repo.example.com'
}
}
}
publishing {
publications {
create<MavenPublication>("bootJava") {
artifact(tasks.named("bootJar"))
}
}
repositories {
maven {
url = uri("https://repo.example.com")
}
}
}
6.2. Distributing with the Application Plugin
When the application
plugin is applied a distribution named boot
is created.
This distribution contains the archive produced by the bootJar
or bootWar
task and scripts to launch it on Unix-like platforms and Windows.
Zip and tar distributions can be built by the bootDistZip
and bootDistTar
tasks respectively.
To use the application
plugin, its mainClassName
property must be configured with the name of your application’s main class.
7. Running your Application with Gradle
To run your application without first building an archive use the bootRun
task:
$ ./gradlew bootRun
The bootRun
task is an instance of BootRun
which is a JavaExec
subclass.
As such, all of the usual configuration options for executing a Java process in Gradle are available to you.
The task is automatically configured to use the runtime classpath of the main source set.
By default, the main class will be configured automatically by looking for a class with a public static void main(String[])
method in the main source set’s output.
The main class can also be configured explicitly using the task’s main
property:
tasks.named("bootRun") {
mainClass = 'com.example.ExampleApplication'
}
tasks.named<BootRun>("bootRun") {
mainClass.set("com.example.ExampleApplication")
}
Alternatively, the main class name can be configured project-wide using the mainClass
property of the Spring Boot DSL:
springBoot {
mainClass = 'com.example.ExampleApplication'
}
springBoot {
mainClass.set("com.example.ExampleApplication")
}
By default, bootRun
will configure the JVM to optimize its launch for faster startup during development.
This behavior can be disabled by using the optimizedLaunch
property, as shown in the following example:
tasks.named("bootRun") {
optimizedLaunch = false
}
tasks.named<BootRun>("bootRun") {
optimizedLaunch.set(false)
}
If the application
plugin has been applied, its mainClass
property must be configured and can be used for the same purpose:
application {
mainClass = 'com.example.ExampleApplication'
}
application {
mainClass.set("com.example.ExampleApplication")
}
7.1. Passing Arguments to your Application
Like all JavaExec
tasks, arguments can be passed into bootRun
from the command line using --args='<arguments>'
when using Gradle 4.9 or later.
For example, to run your application with a profile named dev
active the following command can be used:
$ ./gradlew bootRun --args='--spring.profiles.active=dev'
See the javadoc for JavaExec.setArgsString
for further details.
7.2. Passing System properties to your application
Since bootRun
is a standard JavaExec
task, system properties can be passed to the application’s JVM by specifying them in the build script.
To make that value of a system property to be configurable set its value using a project property.
To allow a project property to be optional, reference it using findProperty
.
Doing so also allows a default value to be provided using the ?:
Elvis operator, as shown in the following example:
tasks.named("bootRun") {
systemProperty 'com.example.property', findProperty('example') ?: 'default'
}
tasks.named<BootRun>("bootRun") {
systemProperty("com.example.property", findProperty("example") ?: "default")
}
The preceding example sets that com.example.property
system property to the value of the example
project property.
If the example
project property has not been set, the value of the system property will be default
.
Gradle allows project properties to be set in a variety of ways, including on the command line using the -P
flag, as shown in the following example:
$ ./gradlew bootRun -Pexample=custom
The preceding example sets the value of the example
project property to custom
.
bootRun
will then use this as the value of the com.example.property
system property.
7.3. Reloading Resources
If devtools has been added to your project it will automatically monitor your application’s classpath for changes. Note that modified files need to be recompiled for the classpath to update in order to trigger reloading with devtools. For more details on using devtools, refer to this section of the reference documentation.
Alternatively, you can configure bootRun
such that your application’s static resources are loaded from their source location:
tasks.named("bootRun") {
sourceResources sourceSets.main
}
tasks.named<BootRun>("bootRun") {
sourceResources(sourceSets["main"])
}
This makes them reloadable in the live application which can be helpful at development time.
8. Ahead-of-Time Processing
Spring AOT is a process that analyzes your code at build-time in order to generate an optimized version of it. It is most often used to help generate GraalVM native images.
The Spring Boot Gradle plugin provides tasks that can be used to perform AOT processing on both application and test code. The tasks are configured automatically when the GraalVM Native Image plugin is applied:
plugins {
id 'org.springframework.boot' version '3.0.17.4'
id 'org.graalvm.buildtools.native' version '0.9.28'
id 'java'
}
plugins {
id("org.springframework.boot") version "3.0.17.4"
id("org.graalvm.buildtools.native") version "0.9.28"
java
}
8.1. Processing Applications
Based on your @SpringBootApplication
-annotated main class, the processAot
task generates a persistent view of the beans that are going to be contributed at runtime in a way that bean instantiation is as straightforward as possible.
Additional post-processing of the factory is possible using callbacks.
For instance, these are used to generate the necessary reflection configuration that GraalVM needs to initialize the context in a native image.
As the BeanFactory
is fully prepared at build-time, conditions are also evaluated.
This has an important difference compared to what a regular Spring Boot application does at runtime.
For instance, if you want to opt-in or opt-out for certain features, you need to configure the environment used at build time to do so.
To this end, the processAot
task is a JavaExec
task and can be configured with environment variables, system properties, and arguments as needed.
The nativeCompile
task of the GraalVM Native Image plugin is automatically configured to use the output of the processAot
task.
8.2. Processing Tests
The AOT engine can be applied to JUnit 5 tests that use Spring’s Test Context Framework.
Suitable tests are processed by the processTestAot
task to generate ApplicationContextInitialzer
code.
As with application AOT processing, the BeanFactory
is fully prepared at build-time.
As with processAot
, the processTestAot
task is JavaExec
subclass and can be configured as needed to influence this processing.
The nativeTest
task of the GraalVM Native Image plugin is automatically configured to use the output of the processAot
and processTestAot
tasks.
9. Integrating with Actuator
9.1. Generating Build Information
Spring Boot Actuator’s info
endpoint automatically publishes information about your build in the presence of a META-INF/build-info.properties
file.
A BuildInfo
task is provided to generate this file.
The easiest way to use the task is through the plugin’s DSL:
springBoot {
buildInfo()
}
springBoot {
buildInfo()
}
This will configure a BuildInfo
task named bootBuildInfo
and, if it exists, make the Java plugin’s classes
task depend upon it.
The task’s destination directory will be META-INF
in the output directory of the main source set’s resources (typically build/resources/main
).
By default, the generated build information is derived from the project:
Property | Default value |
---|---|
|
The base name of the |
|
The group of the project |
|
The name of the project |
|
The version of the project |
|
The time at which the project is being built |
The properties can be customized using the DSL:
springBoot {
buildInfo {
properties {
artifact = 'example-app'
version = '1.2.3'
group = 'com.example'
name = 'Example application'
}
}
}
springBoot {
buildInfo {
properties {
artifact.set("example-app")
version.set("1.2.3")
group.set("com.example")
name.set("Example application")
}
}
}
To exclude any of the default properties from the generated build information, add its name to the excludes.
For example, the time
property can be excluded as follows:
springBoot {
buildInfo {
excludes = ['time']
}
}
springBoot {
buildInfo {
excludes.set(setOf("time"))
}
}
The default value for build.time
is the instant at which the project is being built.
A side-effect of this is that the task will never be up-to-date.
As a result, builds will take longer as more tasks, including the project’s tests, will have to be executed.
Another side-effect is that the task’s output will always change and, therefore, the build will not be truly repeatable.
If you value build performance or repeatability more highly than the accuracy of the build.time
property, exclude the time
property as shown in the preceding example.
Additional properties can also be added to the build information:
springBoot {
buildInfo {
properties {
additional = [
'a': 'alpha',
'b': 'bravo'
]
}
}
}
springBoot {
buildInfo {
properties {
additional.set(mapOf(
"a" to "alpha",
"b" to "bravo"
))
}
}
}
10. Reacting to Other Plugins
When another plugin is applied the Spring Boot plugin reacts by making various changes to the project’s configuration. This section describes those changes.
10.1. Reacting to the Java Plugin
When Gradle’s java
plugin is applied to a project, the Spring Boot plugin:
-
Creates a
BootJar
task namedbootJar
that will create an executable, fat jar for the project. The jar will contain everything on the runtime classpath of the main source set; classes are packaged inBOOT-INF/classes
and jars are packaged inBOOT-INF/lib
-
Configures the
assemble
task to depend on thebootJar
task. -
Configures the
jar
task to useplain
as the convention for its archive classifier. -
Creates a
BootBuildImage
task namedbootBuildImage
that will create a OCI image using a buildpack. -
Creates a
BootRun
task namedbootRun
that can be used to run your application. -
Creates a configuration named
bootArchives
that contains the artifact produced by thebootJar
task. -
Creates a configuration named
developmentOnly
for dependencies that are only required at development time, such as Spring Boot’s Devtools, and should not be packaged in executable jars and wars. -
Creates a configuration named
productionRuntimeClasspath
. It is equivalent toruntimeClasspath
minus any dependencies that only appear in thedevelopmentOnly
configuration. -
Configures any
JavaCompile
tasks with no configured encoding to useUTF-8
. -
Configures any
JavaCompile
tasks to use the-parameters
compiler argument.
10.2. Reacting to the Kotlin Plugin
When Kotlin’s Gradle plugin is applied to a project, the Spring Boot plugin:
-
Aligns the Kotlin version used in Spring Boot’s dependency management with the version of the plugin. This is achieved by setting the
kotlin.version
property with a value that matches the version of the Kotlin plugin. -
Configures any
KotlinCompile
tasks to use the-java-parameters
compiler argument.
10.3. Reacting to the War Plugin
When Gradle’s war
plugin is applied to a project, the Spring Boot plugin:
-
Creates a
BootWar
task namedbootWar
that will create an executable, fat war for the project. In addition to the standard packaging, everything in theprovidedRuntime
configuration will be packaged inWEB-INF/lib-provided
. -
Configures the
assemble
task to depend on thebootWar
task. -
Configures the
war
task to useplain
as the convention for its archive classifier. -
Configures the
bootArchives
configuration to contain the artifact produced by thebootWar
task.
10.4. Reacting to the Dependency Management Plugin
When the io.spring.dependency-management
plugin is applied to a project, the Spring Boot plugin will automatically import the spring-boot-dependencies
bom.
10.5. Reacting to the Application Plugin
When Gradle’s application
plugin is applied to a project, the Spring Boot plugin:
-
Creates a
CreateStartScripts
task namedbootStartScripts
that will create scripts that launch the artifact in thebootArchives
configuration usingjava -jar
. The task is configured to use theapplicationDefaultJvmArgs
property as a convention for itsdefaultJvmOpts
property. -
Creates a new distribution named
boot
and configures it to contain the artifact in thebootArchives
configuration in itslib
directory and the start scripts in itsbin
directory. -
Configures the
bootRun
task to use themainClassName
property as a convention for itsmain
property. -
Configures the
bootRun
task to use theapplicationDefaultJvmArgs
property as a convention for itsjvmArgs
property. -
Configures the
bootJar
task to use themainClassName
property as a convention for theStart-Class
entry in its manifest. -
Configures the
bootWar
task to use themainClassName
property as a convention for theStart-Class
entry in its manifest.
10.6. Reacting to the GraalVM Native Image Plugin
When the GraalVM Native Image plugin is applied to a project, the Spring Boot plugin:
-
Applies the
org.springframework.boot.aot
plugin that:-
Registers
aot
andaotTest
source sets. -
Registers a
ProcessAot
task namedprocessAot
that will generate AOT-optimized source for the application in theaot
source set. -
Configures the Java compilation and process resources tasks for the
aot
source set to depend uponprocessAot
. -
Registers a
ProcessTestAot
task namedprocessTestAot
that will generated AOT-optimized source for the application’s tests in theaotTest
source set. -
Configures the Java compilation and process resources tasks for the
aotTest
source set to depend uponprocessTestAot
.
-
-
Adds the output of the
aot
source set to the classpath of themain
GraalVM native binary. -
Adds the output of the
aotTest
source set to the classpath of thetest
GraalVM native binary. -
Configures the GraalVM extension to disable Toolchain detection.
-
Configures each GraalVM native binary to require GraalVM 22.3 or later.
-
Configures the
bootJar
task to include the reachability metadata produced by thecollectReachabilityMetadata
task in its jar. -
Configures the
bootBuildImage
task to usepaketobuildpacks/builder-jammy-tiny:latest
as its builder and to setBP_NATIVE_IMAGE
totrue
in its environment.