How do you override a Spring Boot managed dependency version in a Gradle build, and what does strictly() add over resolutionStrategy.force?
answer
- ext['spring-framework.version'] = '...' (same names as Maven)
- strictly() = hard constraint, fails on conflict
- force = silent overwrite, hides conflicts
- constraints block ~ Maven dependencyManagement
- Never downgrade Spring Framework under your Boot version
basics
~20 sSimplest: set an extra property matching Boot's version property, e.g. ext['slf4j.version'] = '2.0.13'. You can also use a strict version constraint or resolutionStrategy.force. strictly() fails the build on an incompatible conflict; force silently rewrites the version.
solid answer
~40 sThe Spring Boot Gradle plugin imports spring-boot-dependencies as a platform and, via the same version-property names as Maven, lets you override with extra properties: ext['spring-framework.version'] = '6.1.11'. That's the idiomatic single-point override. For a specific artifact you can pin a rich version with strictly(): implementation('org.slf4j:slf4j-api') { version { strictly('2.0.13') } }. Or apply resolutionStrategy.force / eachDependency across the whole graph. The difference matters: strictly() is a hard constraint — if another dependency demands an incompatible version, Gradle fails the build so you see the conflict. resolutionStrategy.force just overwrites the resolved version and can mask real incompatibilities. Prefer ext-properties for coordinated BOM bumps, strictly() when you must forbid an upgrade, and force sparingly. All of these drift off Boot's tested set.
code
groovy · 23 linesplugins {
id 'org.springframework.boot' version '3.3.2'
id 'io.spring.dependency-management' version '1.1.6'
}
// Option 1: idiomatic property override (whole BOM)
ext['slf4j.version'] = '2.0.13'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
// Option 2: strict pin for one artifact — FAILS build if something conflicts
implementation('org.slf4j:slf4j-api') {
version { strictly('2.0.13') }
}
}
// Option 4: blunt force — silently rewrites, no conflict error
configurations.all {
resolutionStrategy {
force 'org.slf4j:slf4j-api:2.0.13'
}
}go deeper
Know Gradle also manages versions and you can override with an ext property.
Know the ext-property, constraints, and resolutionStrategy.force options and that property names match Maven's.
Explain the strictly() vs force semantics (fail-on-conflict vs silent overwrite) and pick the right tool per situation.
Decide team-wide policy: prefer rich-version/strictly for auditability, restrict force, and gate overrides in CI to protect against drift.
## How Gradle gets Boot's managed versions Applying the `org.springframework.boot` plugin together with `io.spring.dependency-management` (or, in current setups, the Boot plugin's built-in support that imports `spring-boot-dependencies` as a Gradle **platform**) supplies the same pinned versions Maven gets. Your dependencies are written without versions: `implementation 'org.springframework.boot:spring-boot-starter-web'`. ## Override option 1 — extra property (idiomatic) The plugin exposes Boot's version properties as Gradle **extra properties** with identical names to the Maven BOM. Setting one moves the managed version for everything driven by that property: ```groovy ext['slf4j.version'] = '2.0.13' ext['spring-framework.version'] = '6.1.11' ``` This is the cleanest override — one place, whole-BOM consistency — and mirrors the Maven property approach. ## Override option 2 — rich version strictly() Gradle 'rich versions' let you express intent. `strictly` says *this exact version, no negotiation*: ```groovy implementation('org.slf4j:slf4j-api') { version { strictly('2.0.13') } } ``` If any other node in the graph requires a version outside the strict constraint, Gradle **fails the resolution with a conflict error**. That's a feature: you learn immediately that your override collides with something. (You can also use `require`, `prefer`, and `reject`.) ## Override option 3 — constraints block ```groovy dependencies { constraints { implementation('org.slf4j:slf4j-api:2.0.13') } } ``` A constraint influences version selection without forcing a direct dependency, similar to dependencyManagement in Maven. ## Override option 4 — resolutionStrategy.force / eachDependency ```groovy configurations.all { resolutionStrategy { force 'org.slf4j:slf4j-api:2.0.13' eachDependency { details -> if (details.requested.group == 'org.slf4j') { details.useVersion('2.0.13') } } } } ``` `force` **rewrites** the resolved version unconditionally and does *not* fail on conflict — it silently wins. Powerful but blunt: it can hide the fact that another library genuinely needed a different version. ## strictly() vs force — the key contrast - `strictly()` = declarative hard constraint; **conflicting requirement => build fails** (visible, safe). - `resolutionStrategy.force` = imperative override; **conflicting requirement => silently overwritten** (convenient, risky). For auditability and safety, `strictly()`/rich versions are generally preferred over `force`. ## Gotchas - Extra-property names must match Boot's exact property (e.g. `spring-framework.version`, not `spring.version`). - Forcing a **downgrade** of Spring Framework relative to your Boot version is unsupported and often breaks. - Overriding transitive-only libraries (Netty, Reactor, Jackson) can violate binary-compatibility pairings Boot tested together. ## When to use which Coordinated version bump => `ext` property. Forbid an upgrade / pin a security patch and *want* to know about conflicts => `strictly()`. Last-resort blunt override => `force`, documented. Every path leaves the tested set, so keep overrides minimal and justified.
- Why might you prefer strictly() over resolutionStrategy.force?strictly() is a declarative constraint that makes Gradle fail loudly when another dependency needs an incompatible version, so real conflicts surface. force silently overwrites the resolved version and can mask an incompatibility until runtime.
- What is the cleanest way to bump a whole managed BOM (e.g. all Jackson modules) in Gradle?Set the matching extra property, e.g. ext['jackson-bom.version'] = '2.18.1'. Because the plugin drives the managed versions from that property, every artifact it covers moves together consistently.
saying these in an interview costs you the question
- Saying force and strictly behave identically
- Using extra-property names that don't match Boot's (spring.version instead of spring-framework.version)
- Downgrading Spring Framework below the version Boot requires
- Assuming a single-artifact force keeps related modules in sync (it doesn't)