What are shade resource transformers, and why are ServicesResourceTransformer and ManifestResourceTransformer often required?
answer
- same-path files collide -> last wins
- ServicesResourceTransformer concatenates META-INF/services
- SPI/ServiceLoader: JDBC drivers, SLF4J
- ManifestResourceTransformer sets Main-Class
- AppendingTransformer reference.conf
basics
~10 sTransformers control how shade merges resource files that exist in multiple dependency JARs. ServicesResourceTransformer concatenates META-INF/services files so SPI lookups still work; ManifestResourceTransformer builds the merged manifest, e.g. setting Main-Class.
solid answer
~40 sWhen shade flattens many JARs into one, files with the **same path** collide and, by default, one silently overwrites the others — which corrupts files meant to be merged. **Resource transformers** customize merge behavior per path. The two most important: `ServicesResourceTransformer` concatenates `META-INF/services/*` files from all deps, so Java's `ServiceLoader` (used by JDBC drivers, SLF4J bindings, etc.) still discovers every provider; without it you lose all but one provider. `ManifestResourceTransformer` produces a single merged `META-INF/MANIFEST.MF` and lets you set the `Main-Class` so the uber-JAR is runnable. Other common ones: `AppendingTransformer` (e.g. for `reference.conf` in Typesafe Config), `ApacheLicenseResourceTransformer`/`ApacheNoticeResourceTransformer` for license files, and `XmlAppendingTransformer` for Spring schema files. Forgetting transformers is the classic cause of 'works as separate JARs but breaks as an uber-JAR' bugs.
code
xml · 6 lines<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.App</mainClass>
</transformer>
</transformers>go deeper
Knows ManifestResourceTransformer is how you set the runnable main class.
Understands the default last-wins collision and adds ServicesResourceTransformer for SPI/JDBC.
Diagnoses 'works split, breaks shaded' bugs to missing transformers and chooses the right one per resource.
Curates a standard transformer set in a shared parent POM so teams don't rediscover SPI/reference.conf breakage.
## The merge collision problem When shade unpacks N dependency JARs into one, multiple JARs can contain a file at the *same path* — e.g. each JDBC driver ships `META-INF/services/java.sql.Driver`. By default the **last writer wins** and the rest are dropped. For files that are *supposed to be merged*, that silently breaks functionality. **Resource transformers** intercept specific paths and define how to combine them. ## ServicesResourceTransformer — the SPI fixer Java's **Service Provider Interface (SPI)** discovers implementations at runtime via `ServiceLoader`, reading `META-INF/services/<interface-FQN>` files that list provider class names. Many libraries use it (JDBC `java.sql.Driver`, SLF4J/Log4j bindings, Jackson modules, etc.). If two deps both have `META-INF/services/java.sql.Driver`, plain overwrite keeps only one driver. `ServicesResourceTransformer` **concatenates** all such files (and dedups, and rewrites relocated class names inside them). This is the single most common reason an uber-JAR mysteriously can't find a driver/binding. ## ManifestResourceTransformer — the executable maker Every JAR has `META-INF/MANIFEST.MF`. With many deps you must merge into one and usually set the entry point: ```xml <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.App</mainClass> <manifestEntries> <Multi-Release>true</Multi-Release> </manifestEntries> </transformer> ``` This writes `Main-Class: com.example.App` so `java -jar` works. ## Full example ```xml <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.App</mainClass> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>reference.conf</resource> </transformer> </transformers> </configuration> ``` ## Other useful transformers - **AppendingTransformer** — concatenates a named text resource (e.g. `reference.conf`). - **XmlAppendingTransformer** — merges XML (Spring `spring.handlers`/`spring.schemas`). - **ApacheLicenseResourceTransformer / ApacheNoticeResourceTransformer** — collapse duplicate LICENSE/NOTICE files. - **DontIncludeResourceTransformer** — drop matching resources. ## Rule of thumb Any file under `META-INF/services`, plus config files designed to aggregate (`reference.conf`, Spring schema files), needs an explicit transformer or the uber-JAR will silently lose data.
- Why might a shaded app fail with 'No suitable driver found' for JDBC?Multiple deps had META-INF/services/java.sql.Driver and overwrote each other; add ServicesResourceTransformer to concatenate them.
- What's a symptom of a missing AppendingTransformer for reference.conf?Typesafe Config / Akka loses config from all but one library because the reference.conf files overwrote instead of merging.
Merging two libraries' card catalogs: by default you'd throw one catalog away; a transformer instead staples the index cards together so every book is still findable.
saying these in an interview costs you the question
- Saying duplicate resource files are merged automatically (default is overwrite/last-wins)
- Confusing transformers (resource merging) with relocations (package renaming)
- Setting Main-Class in the wrong place when a ManifestResourceTransformer is available