maven-shade-plugin maven-assembly-plugin
When developing Java applications with Maven, one of the most common challenges is how to package your project for deployment. A standard JAR file typically contains only your compiled code, leaving the responsibility of managing dependencies to the runtime environment. However, for modern microservices, CLI tools, or standalone applications, you often need to bundle everything—your code and all its third-party libraries—into a single, portable artifact.
This is where the maven-shade-plugin and the maven-assembly-plugin come into play. While both can produce "fat JARs" (also known as Uber-JARs), they serve fundamentally different purposes and operate on different logic. Understanding their nuances is critical for building robust deployment pipelines.
The maven-shade-plugin is designed with a specific goal in mind: creating a single, highly optimized JAR file that contains your project's classes and all its dependencies. It is the go-to choice when your primary objective is to create an executable "fat JAR" that can run anywhere with a simple java -jar command.
1. Solving "Jar Hell" via Relocation
The most powerful feature of the Shade plugin is relocation. In complex Java ecosystems, you might encounter "dependency conflicts" (often called Jar Hell). For example, your application might require Library A (which uses Guava 18), while another dependency in your project requires Library B (which uses Guava 21). If you simply bundle both, the ClassLoader will struggle to decide which version to use, leading to NoSuchMethodError or ClassNotFoundException.
The Shade plugin solves this by rewriting the bytecode of your dependencies. It can move a specific package from its original location to a new, renamed package (e.g., moving com.google.common to my.app.shaded.com.google.common). This ensures that your application uses its own private, renamed version of the library, completely isolated from any other versions present in the environment.
2. Resource Transformers
When you merge multiple JARs into one, you often run into issues with overlapping files. A classic example is the META-INF/services directory, which is used by the Java Service Provider Interface (SPI). If two different libraries both have a file named META-INF/services/com.example.Plugin, a standard merge would simply overwrite one with the other, breaking one of the libraries.
The Shade plugin provides Resource Transformers that allow you to intelligently merge these files instead of overwriting them. It can append entries to service files, merge properties files, or handle manifest files to ensure that the Main-Class attribute is correctly set for an executable JAR.
3. Minimization and Filtering
To keep the resulting Uber-JAR from becoming unnecessarily bloated, the Shade plugin offers minimization capabilities. It can analyze your code to identify which classes from your dependencies are actually being used and exclude the rest. While this requires careful testing, it can significantly reduce the footprint of your deployment artifact.
The Maven Assembly Plugin: The Distribution Architect
While the Shade plugin focuses on the internal composition of a JAR, the maven-assembly-plugin is focused on the external structure of your distribution. It is a much broader tool designed to package your project into various formats, such as .zip, .tar.gz, or even custom directory structures.
1. Custom Distribution Layouts
If your goal is to provide a professional software release that includes more than just a JAR—such as a bin/ folder for executables, a conf/ folder for configuration files, a lib/ folder for dependencies, and a docs/ folder for documentation—the Assembly plugin is the correct tool.
By using an Assembly Descriptor (an XML file), you can define exactly how every file in your project and its dependencies should be mapped into the final archive. This allows you to create complex, multi-file distributions that mirror how professional software is typically installed on Linux or Windows systems.
2. Flexibility in Packaging
The Assembly plugin is not limited to JARs. It is a general-purpose packaging tool. You can use it to:
- Bundle your project with specific configuration files.
- Create a "dependency-only" ZIP file for a client.
- Generate a complete installation package containing scripts, binaries, and assets.
Unlike the Shade plugin, the Assembly plugin does not inherently "understand" the bytecode of your dependencies. It treats files as units to be moved and organized, rather than code to be merged and relocated.
Key Differences at a Glance
To help you decide which plugin to use for your specific scenario, consider the following comparison:
| Feature | Maven Shade Plugin | Maven Assembly Plugin |
|---|---|---|
| Primary Goal | Creating an optimized, single Uber-JAR. | Creating a complete distribution package. |
| Core Strength | Relocation (resolving dependency conflicts). | Structure (organizing files into ZIP/TAR). |
| Conflict Handling | Merges resources and rewrites bytecode. | Simply copies or moves files into a structure. |
| Output Format | Primarily a single .jar file. |
.zip, .tar.gz, .tar, directories, etc. |
| Best Use Case | Microservices, CLI tools, standalone apps. | Software releases, installer packages. |
Summary: Which One Should You Choose?
The choice depends entirely on your deployment target:
- Choose
maven-shade-pluginif you need a single, self-contained executable JAR and you are worried about library version conflicts or need to merge SPI service files. It is the "surgical" tool for bytecode and dependency management. - Choose
maven-assembly-pluginif you need to deliver a complete software package with a specific folder hierarchy, multiple file types, or non-JAR assets. It is the "architectural" tool for file organization and distribution.
In some advanced CI/CD pipelines, you might even see both used in tandem: the Shade plugin creates the optimized Uber-JAR, and then the Assembly plugin takes that JAR and wraps it into a structured .tar.gz distribution alongside configuration and documentation files.