How to Exclude Transitive Dependencies in Gradle
This guide explains how to exclude transitive dependencies from your project when they are not needed or cause conflicts.
Why Exclude Transitive Dependencies?
Excluding a transitive dependency should be a conscious decision, as removing required dependencies may lead to runtime errors if a library depends on them.
Before excluding, ensure:
-
Your application does not require the excluded dependency.
-
You have sufficient test coverage to verify that excluding the dependency does not break functionality.
Exclusions are useful when:
-
A library includes unnecessary transitive dependencies that are not required by your application.
-
You need to reduce the size of your dependencies.
-
A dependency conflict exists and must be resolved differently.
Step 1: Excluding a Transitive Dependency using exclude()
First you must find the dependency that is causing the unwanted transitive to be used.
You can use the dependencies
task for this.
In this example, we want to remove the commons-collections
dependency:
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4")
}
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4")
}
Running ./gradlew dependencies --configuration runtimeClasspath
showcases how commons-collections
is brought in by commons-beanutils
:
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_exclude_transitive_dependencies'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
\--- commons-beanutils:commons-beanutils:1.9.4
+--- commons-logging:commons-logging:1.2
\--- commons-collections:commons-collections:3.2.2
You can exclude a transitive dependency per dependency declaration by specifying the group
and module
attributes in an exclude()
rule:
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4") {
exclude(group = "commons-collections", module = "commons-collections")
}
}
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4") {
exclude group: "commons-collections", module: "commons-collections"
}
}
-
This removes
commons-collections
from the transitive dependencies ofcommons-beanutils
. -
The exclusion only applies to this specific dependency (i.e.,
commons-beanutils
).
Running ./gradlew dependencies --configuration runtimeClasspath
showcases the results:
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_exclude_transitive_dependencies'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
\--- commons-beanutils:commons-beanutils:1.9.4
\--- commons-logging:commons-logging:1.2
If your application only uses a subset of the library that does not require the excluded dependency, this approach is safe.
Step 2: Understanding the Impact of Exclusions
Exclusions only apply if all dependency declarations agree on the exclusion. If another dependency in your project still requires the excluded dependency, Gradle will not exclude it.
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4") {
exclude(group = "commons-collections", module = "commons-collections")
}
implementation("com.opencsv:opencsv:4.6") // Depends on 'commons-beanutils' but does NOT exclude 'commons-collections'
}
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4") {
exclude group: "commons-collections", module: "commons-collections"
}
implementation("com.opencsv:opencsv:4.6") // Depends on 'commons-beanutils' but does NOT exclude 'commons-collections'
}
In this case, commons-collections
is still included because opencsv
brings it back.
Running ./gradlew dependencies --configuration runtimeClasspath
showcases the results:
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_exclude_transitive_dependencies'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
+--- commons-beanutils:commons-beanutils:1.9.4
| +--- commons-logging:commons-logging:1.2
| \--- commons-collections:commons-collections:3.2.2
\--- com.opencsv:opencsv:4.6
+--- org.apache.commons:commons-lang3:3.8.1
+--- org.apache.commons:commons-text:1.3
| \--- org.apache.commons:commons-lang3:3.7 -> 3.8.1
+--- commons-beanutils:commons-beanutils:1.9.3 -> 1.9.4 (*)
\--- org.apache.commons:commons-collections4:4.2
To fully exclude commons-collections
, you must also exclude it from opencsv
.
Step 3: Exclude a Transitive Dependency for Multiple Dependencies
You must repeat Step 1 for any additional dependency:
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4") {
exclude(group = "commons-collections", module = "commons-collections")
}
implementation("com.opencsv:opencsv:4.6") {
exclude(group = "commons-collections", module = "commons-collections")
exclude(group = "org.apache.commons", module = "commons-collections4") // Watch out for other transitive dependency creep
}
}
dependencies {
implementation("commons-beanutils:commons-beanutils:1.9.4") {
exclude group: "commons-collections", module: "commons-collections"
}
implementation("com.opencsv:opencsv:4.6") {
exclude group: "commons-collections", module: "commons-collections"
exclude group: "org.apache.commons", module: "commons-collections4" // Watch out for other transitive dependency creep
}
}
This example actually goes a step further as com.opencsv:opencsv
actually brings in commons-collections4
.
Now commons-collections
AND commons-collections4
are fully excluded from all dependencies that reference it:
> Task :dependencies
------------------------------------------------------------
Root project 'how_to_exclude_transitive_dependencies'
------------------------------------------------------------
runtimeClasspath - Runtime classpath of source set 'main'.
+--- commons-beanutils:commons-beanutils:1.9.4
| \--- commons-logging:commons-logging:1.2
\--- com.opencsv:opencsv:4.6
+--- org.apache.commons:commons-lang3:3.8.1
+--- org.apache.commons:commons-text:1.3
| \--- org.apache.commons:commons-lang3:3.7 -> 3.8.1
\--- commons-beanutils:commons-beanutils:1.9.3 -> 1.9.4 (*)
Summary
-
Use exclusions only when necessary to avoid runtime errors.
-
Ensure all dependencies agree on an exclusion for it to be effective.
-
Consider alternative Gradle features like dependency constraints or metadata rules before excluding.
-
Use Dependency Constraints - If a dependency version conflict occurs, adjust the version using a constraint rather than excluding the dependency entirely.
-
Apply Component Metadata Rules - If a dependency is incorrectly declared in metadata (e.g., includes an unnecessary compile-time dependency), you can remove the dependency in a component metadata rule instead of excluding it.
-
Resolve Mutually Exclusive Dependency Conflicts - If multiple dependencies conflict because they represent different implementations of the same feature (e.g.,
log4j
vs.log4j-over-slf4j
), it is better to define component capabilities than to exclude one implementation.
-