What does resolutionStrategy.eachDependency do, and when in the build does the closure you pass to it run?
answer
- per-dependency rule on resolutionStrategy
- runs at resolve time, lazily
- DependencyResolveDetails.requested
- useVersion vs useTarget
- configurations.all wrapper
basics
~10 seachDependency registers a rule that Gradle invokes for every dependency while resolving a configuration. The rule receives DependencyResolveDetails and can rewrite the requested version or coordinates before the dependency graph is built.
solid answer
~40 s`resolutionStrategy.eachDependency { ... }` registers a per-dependency rule that runs at **resolve time** — when a configuration's graph is actually computed, not at configuration time. For each requested module (every direct and transitive dependency) Gradle hands you a `DependencyResolveDetails`. From it you read `details.requested` (group/name/version as declared) and mutate the outcome with `details.useVersion("x.y.z")` to change just the version, or `details.useTarget("group:name:version")` to swap the whole coordinate. Because it fires before conflict resolution completes, it's a hook to normalize versions across a graph, redirect a coordinate, or pin a transitive without editing every declaration. You typically attach it via `configurations.all { resolutionStrategy.eachDependency { ... } }` so it applies to every configuration of the project.
code
kotlin · 9 linesconfigurations.all {
resolutionStrategy.eachDependency {
// `this` is DependencyResolveDetails
if (requested.group == "org.slf4j") {
useVersion("2.0.13")
because("standardize SLF4J across the graph")
}
}
}go deeper
Know it's a rule that lets you change a dependency's version during resolution, via useVersion.
Explain resolve-time vs configuration-time, that it fires per module including transitives, and the requested/useVersion/useTarget surface.
Discuss wiring via configurations.all, performance (closure runs many times), and how it relates to but differs from forcing/constraints.
Frame it as a governance hook for normalizing versions across many modules and its tradeoffs vs platform/BOM-based alignment at org scale.
## What it is `eachDependency` is a method on a configuration's `ResolutionStrategy`. It registers a callback that Gradle runs **once for every dependency** (direct and transitive) it encounters while resolving a configuration's dependency graph. It is the imperative, fine-grained escape hatch for rewriting what gets resolved. ## When it runs Gradle has two big phases: **configuration time** (build scripts evaluate, tasks and configurations are defined) and **execution/resolve time** (a configuration's graph is actually computed when something needs it — e.g. compiling, or calling `.resolve()`/`.files`). The `eachDependency` closure runs at **resolve time**, lazily. If a configuration is never resolved, the rule never fires. This matters because at configuration time you don't yet know the full transitive set; the rule lets you react to each module as the resolver walks the graph. ## What you get: DependencyResolveDetails The closure receives a `DependencyResolveDetails` with: - `details.requested` — a `ModuleVersionSelector` exposing `.group`, `.name`, `.version` as **requested** (declared/transitively-asked-for). - `details.target` — the current target after any earlier rules. - `details.useVersion(String)` — keep group/name, change the version only. - `details.useTarget(Object)` — replace the whole coordinate (a `"group:name:version"` string, a map, or a dependency notation). Use this to redirect to a different module entirely. - `details.because(String)` — attach a human-readable reason, surfaced in `dependencyInsight`. ## Typical wiring You usually apply it to all configurations so it's global to the project: ```kotlin configurations.all { resolutionStrategy.eachDependency { if (requested.group == "com.fasterxml.jackson.core") { useVersion("2.17.1") because("align all Jackson core modules") } } } ``` ## Where it sits relative to conflict resolution The rule fires **before** Gradle's default newest-wins conflict resolution settles a single version per module. So you can normalize a version across the graph and let conflict resolution proceed from there. It is *not* a hard force — it expresses "resolve this requested coordinate to that"; other mechanisms (constraints, strict versions) own the 'force/win' semantics. `eachDependency` is the programmatic, per-module rewrite hook. ## Practical uses - Pin a noisy transitive's version without touching every declaration. - Map a legacy coordinate to a relocated one (`useTarget`). - Apply a version derived from a Provider/extension property at resolve time. Keep the closure cheap and side-effect-free: it runs many times, once per module per resolution.
- If a configuration is never resolved, does the eachDependency closure still run?No. The rule fires lazily during resolution of that configuration. No resolution means the closure never executes.
- Why wrap it in configurations.all instead of attaching to one configuration?configurations.all applies the rule to every current and future configuration, so the rewrite is project-wide (e.g. compileClasspath, runtimeClasspath, testRuntimeClasspath) instead of only one.
saying these in an interview costs you the question
- Saying it runs at configuration time — it runs lazily at resolve time.
- Claiming it only sees direct dependencies — it fires for transitives too.