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

Open
#50,762 11 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.