skip to content

How does inheriting the parent make `mvn package` produce an executable fat jar? Explain the repackage goal wiring.

level: seniorimportance: should knowfreq 45%

answer

  1. Thin jar = not runnable
  2. repackage goal bound to package phase
  3. *.jar.original kept
  4. BOOT-INF/lib nested deps + loader
  5. Manifest Main-Class=JarLauncher

basics

~20 s

The parent's plugin management binds the spring-boot-maven-plugin's repackage goal to Maven's package phase. So after Maven builds the plain jar, the plugin rewrites it into a self-contained runnable jar (with nested dependencies and a launcher) automatically.

solid answer

~40 s

Normally `maven-jar-plugin` produces a plain, thin jar during the `package` phase — not runnable, since dependencies aren't included. spring-boot-starter-parent's `<pluginManagement>` pre-declares the `spring-boot-maven-plugin` with an execution whose goal is `repackage` (id `repackage`), bound to the `package` phase. When you enable the plugin in your build (`<plugin>` with no version — it's inherited), that execution takes the plain jar Maven just built, renames it to `*.jar.original`, and rewrites `*.jar` as an **executable, self-contained jar**: it nests your dependency jars under `BOOT-INF/lib`, your classes under `BOOT-INF/classes`, adds the `spring-boot-loader` launcher classes, and sets `Main-Class`/`Start-Class` in the manifest. So plain `mvn package` yields a `java -jar`-runnable artifact with zero extra config.

code

kotlin · 19 lines
kotlin
// Enabling the inherited plugin (XML shown as comment):
//
// <build>
//   <plugins>
//     <plugin>
//       <groupId>org.springframework.boot</groupId>
//       <artifactId>spring-boot-maven-plugin</artifactId>
//       <!-- version + repackage execution inherited from the parent's pluginManagement -->
//     </plugin>
//   </plugins>
// </build>
//
// Result of `mvn package`:
//   target/myapp-1.0.jar          <- executable fat jar (java -jar works)
//   target/myapp-1.0.jar.original <- the plain thin jar before repackage
//
// Fat-jar layout:
//   BOOT-INF/classes/  BOOT-INF/lib/*.jar  org/springframework/boot/loader/...
//   META-INF/MANIFEST.MF: Main-Class=...JarLauncher, Start-Class=com.example.App

go deeper

for a junior

Knows mvn package yields a runnable jar and that the Spring plugin is involved.

for a middle

Explains repackage binds to the package phase and produces the fat jar plus .original.

for a senior

Describes the BOOT-INF layout, loader/JarLauncher, and pluginManagement vs plugins distinction.

for a principal

Reasons about library-vs-app packaging, classifiers, and consequences of overriding the inherited execution.

## The default (non-Spring) situation In a normal Maven build, the `package` phase runs `maven-jar-plugin`, which zips your compiled classes and resources into a **thin jar**. That jar is **not executable**: it doesn't contain your third-party dependencies, and jars can't nest other jars on the standard classpath. Running `java -jar app.jar` would fail with `NoClassDefFoundError`. ## What the parent wires up `spring-boot-starter-parent` contains, in its `<build><pluginManagement>`, a managed entry for `org.springframework.boot:spring-boot-maven-plugin`. That managed entry includes an `<executions>` block: - **goal**: `repackage` - **id**: `repackage` - bound (via plugin default) to the **`package`** phase. Because it's in *plugin management*, it doesn't run until you actually reference the plugin in your project's `<build><plugins>` — but you reference it **without a version and without configuring the execution**, because both are inherited: ```xml <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> ``` ## What `repackage` does During `package`, Maven first builds the thin jar (jar plugin), then the `repackage` goal executes and **transforms** it: 1. Renames the original thin jar to `myapp-1.0.jar.original`. 2. Produces a new `myapp-1.0.jar` in the Spring Boot **executable jar layout**: - `BOOT-INF/classes/` — your application classes and resources. - `BOOT-INF/lib/` — every dependency jar, **nested** inside. - `org/springframework/boot/loader/...` — the `spring-boot-loader` launcher. - `META-INF/MANIFEST.MF` with `Main-Class: org.springframework.boot.loader.launch.JarLauncher` and `Start-Class:` = your `@SpringBootApplication` class. 3. The loader's `JarLauncher` can read nested jars from `BOOT-INF/lib` at runtime — something the standard JVM classloader can't do — making `java -jar myapp-1.0.jar` work. ## Why the parent matters here Without the parent, you'd have to declare the plugin version *and* manually add the `<execution>` binding `repackage` to `package` yourself. The parent removes that boilerplate and keeps the plugin version aligned with your Boot version. ## Gotchas - If you reference the plugin but the `repackage` execution somehow isn't active (e.g. you overrode executions), `mvn package` yields only the thin, non-runnable jar. - The `.original` file is the pre-repackage thin jar; some setups need it (e.g. as a dependency for other modules) — for that, libraries often use the plugin's `classifier` so the fat jar and the plain jar coexist. - For **library** modules (not apps) you typically **don't** want repackage at all, since a fat jar isn't a good dependency; you'd disable the execution. - The plugin also contributes `build-info`, `run`, and (Boot 2.3+) image-building goals, but only `repackage` is wired to `package` by default.

  • What is the *.jar.original file and why is it kept?
    It's the plain thin jar produced by maven-jar-plugin before repackage rewrote the main jar. It's preserved so the original artifact isn't lost (and can be referenced/installed if needed).
  • For a shared library module, would you want the repackage goal active?
    No. A fat jar is a poor dependency (nested layout, bundled transitive deps). You disable the repackage execution so the module publishes a normal thin jar consumable by other modules.

saying these in an interview costs you the question

  • Thinking maven-jar-plugin alone makes an executable jar
  • Claiming the fat jar puts dependencies on the flat classpath rather than nested BOOT-INF/lib
  • Believing repackage runs even without referencing the plugin in <plugins>

context