Resolving Incompatible Jar Dependency Conflicts in Java Projects Using Maven Shade Plugin
This article discusses the challenges of incompatible Jar version conflicts in Java projects, explores classloader isolation approaches like Pandora and SOFAArk, and provides a step‑by‑step guide to using the Maven Shade Plugin to relocate and package conflicting Guava libraries into a custom wrapper jar.
In Java development, encountering ClassNotFoundException or NoSuchMethodException often points to jar version conflicts, where different parts of a project depend on different versions of the same library. When a business dependency and a middleware dependency require incompatible versions of a jar (e.g., Guava 0.1 vs 0.2), developers face a dilemma similar to choosing which to rescue when both a wife and mother fall into water.
The article first considers copying the needed high‑version Guava code directly into the project, but this approach is heavy‑weight due to transitive dependencies and maintenance overhead.
It then examines classloader isolation solutions. Alibaba’s internal Pandora component (and its open‑source counterpart SOFAArk) can load different versions of third‑party dependencies in separate classloaders, effectively isolating them. However, adopting such a solution requires packaging dependencies as Ark plugins, which may be excessive for a single project.
Finally, the article presents the Maven Shade Plugin as a lightweight, practical solution. By configuring the plugin to relocate the packages of the conflicting jar, developers can create a custom wrapper jar (e.g., guava-wrapper ) that coexists with the middleware’s version without class conflicts.
Below is a complete pom.xml configuration for the Maven Shade Plugin, including dependency declarations, build plugins, and relocation rules that rename com.google.guava , com.google.common , and com.google.thirdparty packages to custom namespaces.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.shaded.example</groupId>
<artifactId>guava-wrapper</artifactId>
<version>${guava.wrapper.version}</version>
<name>guava-wrapper</name>
<url>https://example.com/guava-wrapper</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<guava.wrapper.version>27.1-jre</guava.wrapper.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<executions>
<execution>
<id>default-jar</id>
<goals>
<goal>jar</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>com.google.guava</pattern>
<shadedPattern>com.google.guava.wrapper</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>com.google.common.wrapper</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.thirdparty</pattern>
<shadedPattern>com.google.wrapper.thirdparty</shadedPattern>
</relocation>
</relocations>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"/>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>After building and publishing the guava-wrapper artifact, developers simply import classes from the new package (e.g., com.google.guava.wrapper ) in their code, avoiding the original conflict.
The conclusion emphasizes that while manual code copying or heavyweight classloader isolation can solve the problem, the Maven Shade Plugin offers a lightweight, maintainable, and elegant solution for most scenarios. For widespread or systemic jar conflicts, more robust isolation frameworks like Pandora or SOFAArk may still be appropriate.
References to additional technical articles are provided at the end of the original document.
High Availability Architecture
Official account for High Availability Architecture.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.