Compare the ways to produce a runnable Java distribution with Maven: shade/assembly uber-jars, Spring Boot repackage, and OS-native distributions via appassembler, jlink, or jpackage. When would you pick each?
answer
- shade = relocate + transformers (services merge)
- boot repackage nests, no merge
- appassembler = scripts + lib/
- jlink = trimmed custom JRE
- jpackage = native installer w/ runtime
basics
~20 sUber-jars (shade/assembly) and Spring Boot repackage bundle everything into one runnable jar needing only a JRE. appassembler generates start scripts plus a lib folder. jlink builds a trimmed custom runtime image, and jpackage produces a native OS installer or app bundle that includes a runtime, so users need no separate Java install.
solid answer
~50 sThere's a spectrum. maven-shade-plugin and maven-assembly-plugin flatten all dependency classes into a single self-contained jar (with a Main-Class manifest) — simple to ship but they merge classes, so you need transformers (e.g., ServicesResourceTransformer) and relocation to handle resource collisions and shading conflicts. Spring Boot's repackage avoids merging by nesting whole jars and a custom classloader. appassembler-maven-plugin doesn't make one jar; it emits cross-platform launch scripts plus a lib/ directory of jars — good for traditional server installs. jlink (via moduleplugin/jlink-plugin) builds a minimal custom JRE image containing only required modules, shrinking footprint for containers. jpackage goes furthest: it produces platform-native installers (.msi/.pkg/.deb) or app images bundling a runtime, so end users need no JDK. Choose uber-jar/repackage for cloud services run with java -jar, jlink for slim container images, and jpackage for distributing desktop or self-contained apps to non-developers.
code
bash · 8 lines# shade/assembly/boot -> one runnable jar (needs a JRE)
java -jar target/app.jar
# jlink -> custom minimal runtime image
jlink --add-modules com.example.app --output dist/runtime
# jpackage -> native installer bundling a runtime (no JRE needed by user)
jpackage --name App --input target --main-jar app.jar --type dmggo deeper
Know that a fat/uber jar bundles dependencies so java -jar runs it.
Explain shade transformers/relocation vs assembly, and that jpackage bundles a runtime.
Pick the right tool per deployment target and justify the trade-offs (footprint, modularity, audience).
Define org distribution standards across cloud, container, and desktop, accounting for JPMS, supply-chain, and runtime-size economics.
## The distribution spectrum From 'one jar you run with an existing JRE' to 'native installer that includes everything': ### 1. Uber/fat jars — maven-shade-plugin & maven-assembly-plugin Both bundle your code + dependencies into a single runnable jar (a `Main-Class` in the manifest makes `java -jar` work). - **maven-assembly-plugin** with the `jar-with-dependencies` descriptor **unpacks** all dependency jars and re-zips their classes flat. - **maven-shade-plugin** does the same but adds two critical features: **relocation** (rename packages, e.g., shade `com.google.guava` to `myapp.shaded.guava` to dodge version clashes) and **transformers** (merge files that collide, e.g., `ServicesResourceTransformer` to merge `META-INF/services` SPI files, `ManifestResourceTransformer` to set Main-Class). Without transformers, the last-wins file overwrite silently breaks SPI lookups and signed jars. ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions><execution><phase>package</phase><goals><goal>shade</goal></goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.Main</mainClass> </transformer> </transformers> </configuration> </execution></executions> </plugin> ``` ### 2. Spring Boot repackage Nests **whole** dependency jars (no merging) + a custom classloader. Best for Boot apps; avoids shade's collision pitfalls. ### 3. appassembler-maven-plugin Does **not** make one jar. It generates **launch scripts** (`bin/app`, `bin/app.bat`) and a `lib/` folder containing each dependency jar, plus optionally bundles a JRE. Classic for traditional on-prem server installs where ops want a folder layout and a start script. ### 4. jlink A JDK tool (driven by `org.moditect`/`maven-jlink`/module plugins) that, for **modular** apps, assembles a **custom, minimal Java runtime image** containing only the JDK modules your app actually uses. Result: a self-contained runtime directory much smaller than a full JRE — ideal for slim Docker images. ### 5. jpackage The JDK's packaging tool (often invoked via exec or a wrapper). It produces **native, platform-specific installers** — `.msi`/`.exe` (Windows), `.pkg`/`.dmg` (macOS), `.deb`/`.rpm` (Linux) — or an app image, **bundling a Java runtime** inside. The end user installs and runs your app like any native program; **no separate Java install needed**. Often combined with jlink to embed a trimmed runtime. ## Choosing - Cloud/microservice run with `java -jar`: Spring Boot repackage, or shade/assembly for non-Boot apps. - Slim container image / footprint-sensitive: **jlink** custom runtime. - Ship to non-developers / desktop: **jpackage** native installer. - Traditional server install with scripts + lib folder: **appassembler**. ## Pitfalls - Shade without transformers → broken SPI (`META-INF/services`) and overwritten resources. - jlink requires the app and its deps to be properly modularized (JPMS) or use automatic modules carefully. - jpackage targets one OS per run — you build on (or for) each target platform.
- Why is the ServicesResourceTransformer needed when shading?Multiple dependency jars each ship META-INF/services/<interface> SPI files; a plain merge overwrites them so only one survives. The transformer concatenates them so all service providers remain discoverable.
- How do jlink and jpackage relate?jlink builds a trimmed custom Java runtime; jpackage can embed that runtime in a native OS installer, so they're often chained to ship a small self-contained native app.
saying these in an interview costs you the question
- Thinking shade and assembly are identical (assembly lacks relocation/transformers)
- Believing jlink produces an installer (it produces a runtime image)
- Assuming jpackage can build all OS installers from one machine in one run
- Forgetting shade collisions break SPI/signed jars