skip to content

In Gradle, what are the two ways to consume the Spring Boot BOM, and how do they differ?

level: seniorimportance: should knowfreq 55%

answer

  1. platform() = Gradle-native constraints
  2. io.spring.dependency-management = Maven emulation
  3. enforcedPlatform() = force like Maven
  4. Boot Gradle plugin auto-imports BOM
  5. constraints can upgrade; Maven BOM wins

basics

~10 s

Either use Gradle's native platform() (e.g. implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.4"))) or apply the io.spring.dependency-management plugin. platform() is Gradle-native; the plugin mimics Maven-style dependency management.

solid answer

~40 s

Gradle has two mechanisms. The native one is platform(): you add implementation(platform("org.springframework.boot:spring-boot-dependencies:<ver>")) which imports the BOM as a Gradle platform, aligning versions across the configuration so you can declare starters without versions. The other is Spring's io.spring.dependency-management plugin, which reproduces Maven's dependencyManagement semantics inside Gradle — it was the historical approach because older Gradle lacked BOM support. Since the Spring Boot Gradle plugin auto-imports the spring-boot-dependencies BOM, if io.spring.dependency-management is applied you often don't declare it manually. Key differences: native platform() honors Gradle's strict conflict resolution (it constrains, and by default managed versions are recommendations that Gradle's resolution can still upgrade for conflicts), whereas io.spring.dependency-management enforces the exact BOM version like Maven (managed version wins unless you override). Modern guidance favors native platform(); you override by declaring an explicit version or a resolution strategy.

code

kotlin · 17 lines
kotlin
// build.gradle.kts — native platform() approach (recommended)
plugins {
    id("org.springframework.boot") version "3.3.4"
    kotlin("jvm") version "2.0.20"
    // no io.spring.dependency-management needed if you import platform yourself
}

dependencies {
    // Import the Spring Boot BOM as a Gradle platform
    implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.4"))

    implementation("org.springframework.boot:spring-boot-starter-web")      // version from BOM
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")     // version from BOM

    // Override a managed version explicitly:
    // implementation("com.fasterxml.jackson.core:jackson-databind:2.18.0")
}

go deeper

for a junior

Know the one-liner implementation(platform("...spring-boot-dependencies:ver")).

for a middle

Distinguish the plugin approach from native platform() and when each is used.

for a senior

Explain constraint-vs-force semantics and enforcedPlatform, plus override paths.

for a principal

Weigh Maven-parity migration, conflict-resolution surprises, and single-sourcing the BOM version across a multi-module Gradle build.

## Why two mechanisms exist Maven has had `<dependencyManagement>` + BOM import forever. Gradle only gained **native BOM support** (the `platform`/`enforcedPlatform` notations and dependency constraints) in Gradle 5. Before that, Spring shipped the **`io.spring.dependency-management`** Gradle plugin to emulate Maven's behavior. Both still exist, so you must know both. ## Option A — native `platform()` ```kotlin dependencies { implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.4")) implementation("org.springframework.boot:spring-boot-starter-web") // no version } ``` `platform(...)` imports the BOM and turns its entries into **dependency constraints** on that configuration. Constraints influence version selection but participate in Gradle's normal conflict resolution — a constraint says 'use this version' but Gradle's resolution can still pick a *higher* version if another constraint/dependency demands it. `enforcedPlatform(...)` is the stricter variant: it turns entries into **forced** versions (like Maven), overriding transitive requests, at the cost of possibly forcing downgrades and breaking other libraries. ## Option B — `io.spring.dependency-management` plugin ```kotlin plugins { id("io.spring.dependency-management") version "1.1.6" } dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:3.3.4") } } ``` This plugin reproduces **Maven semantics**: the BOM's managed version *always wins* over transitive versions (Maven's dependencyManagement is authoritative, unlike Gradle's nearest-wins). It also supports Maven-style property overrides via `ext["..."]` and exclusions. ## The Spring Boot Gradle plugin's role When you apply `org.springframework.boot` (the Boot Gradle plugin), it **automatically imports the spring-boot-dependencies BOM** — via `io.spring.dependency-management` if that plugin is applied, otherwise you rely on it being present. In older setups the Boot plugin required `io.spring.dependency-management`; modern Boot works with native `platform()` too, and current docs show declaring `platform(SpringBootPlugin.BOM_COORDINATES)` or just relying on the auto-import. ## Differences that matter | Aspect | platform() (native) | io.spring.dependency-management | |---|---|---| | Version semantics | Constraints; resolution can upgrade | Maven-style: BOM version wins | | enforced variant | enforcedPlatform() forces | always enforcing | | Property overrides (`<lib>.version`) | via constraints/`ext` differently | supported Maven-style | | Gradle-native tooling | Yes | Emulation layer | ## Overriding a managed version - With **platform()**: declare an explicit version on the dependency, or add a `constraints { }` block, or use `resolutionStrategy`. With Spring's Gradle plugin conventions you can also set an ext property like `set("jackson.version", "...")` — but that only works when the dependency-management plugin/Boot conventions read it. - With **io.spring.dependency-management**: set `ext["jackson-bom.version"] = "..."` or a `dependencies { dependency("...") }` override block. ## Gotchas - Mixing `enforcedPlatform` can silently downgrade libraries and cause runtime breaks — prefer `platform` unless you truly need to force. - If both the Boot plugin and a manual `platform()` import the same BOM at different versions, you can get conflicts; keep the version single-sourced. - Native constraints do **not** replicate Maven's 'managed always wins'; a transitive dependency can pull a newer version than the BOM lists, which surprises Maven-trained developers. ## When to use which New projects: prefer native `platform()` (fewer plugins, Gradle-idiomatic). Migrating Maven builds or needing exact Maven parity/property overrides: `io.spring.dependency-management`.

  • Why might a transitive dependency end up at a NEWER version than the BOM lists when using native platform()?
    Because platform() entries are constraints, not forced versions. Gradle's conflict resolution picks the highest requested compatible version, so another dependency demanding a newer version can upgrade past the BOM's recommendation. Use enforcedPlatform() to force the BOM version.
  • Do you still need io.spring.dependency-management with recent Spring Boot?
    Not necessarily. The Boot Gradle plugin auto-imports the BOM, and native platform() covers version management. The plugin remains useful for Maven-style property overrides and exact Maven parity.

saying these in an interview costs you the question

  • Claiming platform() forces versions exactly like Maven (it constrains; enforcedPlatform forces).
  • Thinking io.spring.dependency-management is required in all Gradle Boot builds.
  • Confusing platform() with enforcedPlatform() semantics.

context