With the following build.gradle.kts
import org.springframework.boot.gradle.plugin.SpringBootPlugin
plugins {
java
id("org.springframework.boot") version "3.3.1"
// (disabled) id("io.spring.dependency-management") version "1.1.5"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation(platform(SpringBootPlugin.BOM_COORDINATES))
implementation("org.springframework.boot:spring-boot-starter-web")
developmentOnly("org.springframework.boot:spring-boot-devtools")
}
./gradlew bootRun works, but ./gradlew bootJar or bootImage fails with:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':bootJar'.
> Could not resolve all files for configuration ':developmentOnly'.
> Could not find org.springframework.boot:spring-boot-devtools:.
Required by:
project :
Everything works when I un-comment id("io.spring.dependency-management") version "1.1.5" but since I'm using Gradle's platform(...) support I didn't really expect to need it, especially because it works until I try to build for production.
Another work-around is to add developmentOnly(platform(SpringBootPlugin.BOM_COORDINATES)) but this is confusing why it's only necessary when the dependencies are not being used (i.e. prod build).
Is this intentional? If so I think it deserves a mention in dev-services docs, which does mention the spring-boot plugin but not dependency-management.
Comment From: wilkinsona
Unlike the dependency management plugin that applies dependency management to every configuration, a platform dependency only applies to the configuration in which it has been declared and those that inherit from it. If you run ./gradlew dependencies --configuration developmentOnly, you'll see that its resolution fails:
developmentOnly - Configuration for development-only dependencies such as Spring Boot's DevTools.
\--- org.springframework.boot:spring-boot-devtools FAILED
It works some of the time depending on the exact structure of what's being resolved and the inter-relationships between the configurations. However, you should not rely upon this and should consider it an implementation detail. Instead, you should repeat the declaration of the platform dependency in the developmentOnly configuration.
The need to declare the platform dependency multiple times is just how things work in Gradle and isn't specific to Spring Boot and its developmentOnly configuration. You'll see the same if, for example, you declare an annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") dependency. You also need to declare an annotationProcessor(platform(SpringBootPlugin.BOM_COORDINATES)) dependency for the annotationProcessor configuration to resolve all of the time.