skip to content

What is package relocation (shading) in maven-shade-plugin and what problem does it solve?

level: seniorimportance: must knowfreq 65%

answer

  1. rename packages + rewrite bytecode refs
  2. private shaded prefix
  3. solves diamond/version conflict
  4. pattern + shadedPattern
  5. breaks reflection/Class.forName

basics

~10 s

Relocation rewrites a dependency's package names (and the bytecode references to them) to a new prefix inside your uber-JAR. It prevents version conflicts when consumers also use a different version of that same library.

solid answer

~40 s

Package relocation is shade's `<relocation>` feature: it physically renames packages inside the uber-JAR, e.g. moving `com.google.common` to `myapp.shaded.com.google.common`, and rewrites every bytecode reference so the moved classes still resolve. This solves **dependency hell / diamond conflicts**: if you publish a library that needs Guava 31 but the consuming app uses Guava 21, both versions can coexist because yours is hidden under a private prefix. It is essential for libraries and plugins that want to bundle dependencies without leaking them onto the consumer's classpath. You configure `<pattern>` (original) and `<shadedPattern>` (target), optionally with `<includes>`/`<excludes>` to scope it. Caveats: relocation can break code that loads classes by string name via reflection, and it doesn't rewrite class names embedded in config/resource files unless you handle them.

code

xml · 4 lines
xml
<relocation>
  <pattern>org.apache.commons.lang3</pattern>
  <shadedPattern>myapp.shaded.commons.lang3</shadedPattern>
</relocation>

go deeper

for a junior

Has heard relocation renames packages to avoid clashes.

for a middle

Can write a <relocation> with pattern/shadedPattern and excludes.

for a senior

Explains the diamond-conflict motivation, scopes relocation precisely, and anticipates reflection breakage.

for a principal

Defines policy: which shared deps (Guava, Netty, protobuf) the org always relocates in published libs, and documents the shaded-prefix namespace convention.

## The problem: classpath conflicts The JVM loads a class by its **fully-qualified name**; only one version of a given class can win on a flat classpath. If your library bundles Guava 31 and the application also depends on Guava 21, both define `com.google.common.collect.ImmutableList` — only one is loaded, and the other code may break (NoSuchMethodError). This is **dependency hell**. ## What relocation does **Relocation** (the original meaning of "shading") rewrites package names *inside your uber-JAR* and rewrites all bytecode references to them, so your private copy lives under a unique prefix nobody else uses: ``` com.google.common.* -> com.myapp.shaded.com.google.common.* ``` Now your code uses `com.myapp.shaded...` and the consumer's Guava 21 stays at `com.google.common...` — no collision. ## Configuration ```xml <configuration> <relocations> <relocation> <pattern>com.google.common</pattern> <shadedPattern>com.myapp.shaded.com.google.common</shadedPattern> <excludes> <exclude>com.google.common.annotations.*</exclude> </excludes> </relocation> </relocations> </configuration> ``` - `<pattern>` — package prefix to move. - `<shadedPattern>` — destination prefix. - `<includes>`/`<excludes>` — fine-grained class selection within the pattern. ## When to use it - **Libraries / Maven plugins / Spark or Hadoop jobs** that must bundle a dependency but cannot dictate the consumer's version. - Avoiding conflicts with the host platform (e.g. a Spark cluster already ships Guava). ## Pitfalls - **Reflection / Class.forName("com.google.common...")**: string class names are not bytecode references, so shade can't rewrite them — relocation breaks them. - **Resource files / service files** that name classes need transformers or manual handling. - **Over-relocating** bloats and complicates debugging; relocate only what conflicts. - Relocation rewrites references *within the shaded jar only*; it does not touch the consumer's classes.

  • Why can relocation break reflection?
    Shade rewrites bytecode references but not class names stored as strings; a Class.forName("com.google.common.X") still points at the old, now-missing, package.
  • Should an application's final uber-JAR usually relocate everything?
    No. Relocation matters mainly for reusable libraries/plugins exposed to others' classpaths; a leaf application has full control of its classpath and rarely needs it.

Like renaming your own private copy of a shared tool and stamping your name on it, so it never gets confused with the building's communal one.

saying these in an interview costs you the question

  • Saying relocation just changes the JAR file name
  • Claiming it rewrites class names inside resource/properties files automatically
  • Confusing relocation (renaming packages) with minimizeJar (removing classes)

context