You cannot control order of Gradle dependencies

May 24, 2023

You cannot control the order of Gradle dependencies.

You will read that you can, it’s not true.
You will read that the order you specify the dependencies is the order they are used when resolved, also not true.
You will try some tricks thinking the order is alphabetical, or as specified, both not true.
You will ask about it and get judgy responses about how it is bad practice and you shouldn’t ever need to do that, don’t have the same class in two places… and so on.

This is mostly nonsense covering for a deficiency in Gradle, Java clearly supports the idea of class dependency ordering by supporting an ordered/hierarchical list of classloaders, as well as the JVM classpath having ordered entries.

But you still want to use Gradle, because it is otherwise excellent, so you need a way to move forward…

The answer is to adjust your dependency jars to avoid the conflict. For example if you have a class A.class that is in a third-party dependency jar and a local project override of that same class, and you want to be sure your local override is always used, then do something like this:
(original credit to Lance)

// create a new config, of the jar we want to trim
// no transitive dependencies, only that one jar
val jarTrimConfig: Configuration by configurations.creating
jarTrimConfig.isTransitive = false

dependencies {
  // identify the one jar that needs trimming
  jarTrimConfig("third-party:package:1.0")
  // add the trimmed jar to our classpath
  implementation(files(trimJar))
}
// task to create the trimmed jar
val trimJar = tasks.register<Jar>("trimJar") {
  archiveFileName.set("$buildDir/libs/trimmed-package.jar")
  from(zipTree(jarTrimConfig.singleFile))
  exclude("third-party/package/A.class")
}