spring-projects / spring-projects/spring-boot
Dependencies in testAndDevelopmentOnly leak into bootJar when a different version of the same dependency is on the runtime classpath
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 81.5k
- Forks
- 42.7k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 65
Description
Spring Boot version
4.1.0
Problem
When a dependency declared as testAndDevelopmentOnly transitively pulls org.springframework.boot:spring-boot-testcontainers with compile scope in its POM, the artifact ends up inside the fat jar produced by bootJar. The Spring Boot BOM (4.1.0) constrains org.testcontainers:testcontainers to test scope, so the actual Testcontainers library is not included. The result is a NoClassDefFoundError: org/testcontainers/lifecycle/Startable at startup — thrown from TestcontainersLifecycleBeanPostProcessor which is present in the jar but missing its dependency.
This regression is triggered by the combination of Spring Boot Gradle plugin 4.1.0 and Spring Boot BOM 4.1.0. Neither alone reproduces it:
| Spring Boot Gradle plugin | Spring Boot BOM | Leak? |
|---|---|---|
| 4.0.6 | 4.0.6 | No |
| 4.1.0 | 4.0.6 | No |
| 4.0.6 | 4.1.0 | No |
| 4.1.0 | 4.1.0 | Yes |
Reproduction
Add a library that declares spring-boot-testcontainers as compile-scoped (e.g. io.arconia:arconia-dev-services-core:0.27.1) via testAndDevelopmentOnly:
// build.gradle.kts
plugins {
id("org.springframework.boot") version "4.1.0"
}
dependencies {
testAndDevelopmentOnly("io.arconia:arconia-dev-services-rabbitmq:0.27.1")
}
Run ./gradlew bootJar and inspect:
jar tf build/libs/app.jar | grep testcontainers
# BOOT-INF/lib/spring-boot-testcontainers-4.1.0.jar ← present
# org.testcontainers:testcontainers ← absent
Start the app:
Caused by: java.lang.NoClassDefFoundError: org/testcontainers/lifecycle/Startable
at org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleBeanPostProcessor.postProcessAfterInitialization(TestcontainersLifecycleBeanPostProcessor.java:86)
Expected behavior
testAndDevelopmentOnly exclusion prevents spring-boot-testcontainers from entering bootJar, as it did with plugin 4.0.6.
Workaround
configurations.named("runtimeClasspath") {
exclude(group = "org.springframework.boot", module = "spring-boot-testcontainers")
exclude(group = "org.testcontainers")
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Kotlin DSL reproduction in build.gradle.kts and inspect bootJar and runtimeClasspath dependency resolution for the 4.1.0 plugin/BOM combination. Compare the listed 4.0.6 and 4.1.0 matrix; done when bootJar excludes spring-boot-testcontainers and its transitive Testcontainers artifacts from testAndDevelopmentOnly without the workaround, and startup no longer raises the shown NoClassDefFoundError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring-boot
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100