How Spring Boot’s Fat Jar Enables One‑Click Java Deployment

Spring Boot’s Fat Jar packages all application code, dependencies, and an embedded server into a single executable JAR, breaking the parent‑delegation model with a custom class loader and protocol handler, enabling simple ‘java -jar’ execution, streamlined deployment, and benefits for microservices and Docker layering.

Cognitive Technology Team
Cognitive Technology Team
Cognitive Technology Team
How Spring Boot’s Fat Jar Enables One‑Click Java Deployment

In Java development, traditional project deployment separates application code and dependencies, using the -cp option to specify the classpath. Spring Boot’s Fat Jar (a self‑contained executable JAR) allows developers to run the entire application with java -jar app.jar. This article analyzes the runtime principles of Spring Boot Fat Jar and how it achieves “out‑of‑the‑box” usability.

1. Core Design Concept of Fat Jar

1.1 What Is a Fat Jar?

A Fat Jar (full name “Fat JAR”) is a self‑contained Java archive whose key characteristics are:

Embedded all dependencies : project code, third‑party libraries (e.g., Spring framework, database drivers), and an embedded server (Tomcat, Jetty) are packaged into a single JAR.

Self‑contained execution : no external dependencies or extra configuration are required; the application runs directly with the java -jar command.

1.2 Structure of a Spring Boot Fat Jar

Spring Boot builds a Fat Jar via Maven or Gradle plugins. Its directory layout is:

express-locker.jar
├── BOOT-INF/
│   ├── classes/      (compiled project classes)
│   └── lib/         (all dependency JARs, e.g., sms-service.jar, payment.jar)
├── META-INF/
│   └── MANIFEST.MF  (metadata specifying the entry point)
└── org/springframework/boot/loader/ (Spring Boot launcher and class loader)

Key components:

BOOT-INF/classes : stores the project's compiled classes.

BOOT-INF/lib : stores all dependency JARs.

org.springframework.boot.loader : custom launcher and class loader that break the parent‑delegation model and load nested JARs.

2. Breaking the Parent‑Delegation Model: Solving Nested JAR Loading

2.1 Limitations of the Java Class‑Loading Mechanism

Java’s native class loaders (Bootstrap, Extension, Application) follow the parent‑delegation model, which prioritizes parent loaders. While this ensures core class safety, it cannot directly load nested JARs (JARs inside other JARs). For example, BOOT-INF/lib/sms-service.jar is a nested JAR that standard loaders cannot access.

2.2 Spring Boot’s Solution

2.2.1 Custom Class Loader: LaunchedURLClassLoader

Spring Boot defines a custom class loader LaunchedURLClassLoader with the following core logic:

public class LaunchedURLClassLoader extends URLClassLoader {
    // Override loadClass to break parent delegation
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 1. Check if already loaded
            Class<?> loadedClass = findLoadedClass(name);
            if (loadedClass != null) return loadedClass;
            // 2. Check hidden packages (e.g., java., javax.)
            if (isHidden(name)) {
                return super.loadClass(name, resolve);
            }
            // 3. Prefer classes from BOOT-INF/classes
            try {
                Class<?> localClass = findClass(name);
                if (localClass != null) return localClass;
            } catch (ClassNotFoundException ex) {}
            // 4. Try parent loader
            try {
                return Class.forName(name, false, getParent());
            } catch (ClassNotFoundException ex) {}
            // 5. Finally load from BOOT-INF/lib (nested JARs)
            return findClass(name);
        }
    }
}

Loading order:

Prefer BOOT-INF/classes (project code).

Attempt loading from the parent loader (e.g., JVM built‑in classes).

Finally load nested JARs from BOOT-INF/lib (dependencies).

2.2.2 Custom Protocol Handler: Supporting Nested JAR Resource Access

Spring Boot extends JarURLConnection to parse nested JAR URLs such as

jar:file:express-locker.jar!/BOOT-INF/lib/sms-service.jar!/templates/alert.txt

, enabling multi‑level resource loading.

public class JarURLConnection extends java.net.JarURLConnection {
    public InputStream getInputStream() throws IOException {
        String nestedPath = getEntryName();
        if (nestedPath.contains("!/")) {
            String[] parts = nestedPath.split("!/", 2);
            JarFile outerJar = new JarFile(parts[0]);
            JarEntry nestedEntry = outerJar.getJarEntry(parts[1]);
            return new NestedJarInputStream(outerJar, nestedEntry);
        }
        return super.getInputStream();
    }
}

3. Startup Process: From java -jar to Application Run

3.1 Key Role of MANIFEST.MF

The MANIFEST.MF file is the entry configuration for a Spring Boot Fat Jar. Important attributes include:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/

Main-Class : specifies the JVM entry class ( JarLauncher).

Start-Class : specifies the Spring Boot application’s main class ( Application), which JarLauncher invokes via reflection.

3.2 Startup Flow Analysis

JVM launch : executing java -jar express-locker.jar loads JarLauncher and its main method.

Load dependencies : JarLauncher uses LaunchedURLClassLoader to load all JARs under BOOT-INF/lib.

Start embedded server : if the project is a web application, Spring Boot automatically starts the embedded Tomcat/Jetty server (default port 8080).

Execute main class : via reflection, the Start-Class ’s main method is invoked, launching the Spring Boot application.

4. Real‑World Scenarios and Advantages

4.1 Rapid Deployment for Microservices and Standalone Apps

One‑click run : no complex classpath or dependency management; simply execute java -jar.

Version isolation : nested JARs avoid conflicts with system‑wide dependencies.

4.2 Convenience of Embedded Servers

Traditional Java web apps require deployment to an external server (e.g., Tomcat). Spring Boot Fat Jar bundles an embedded server, allowing developers to deliver a complete web service with only code changes.

4.3 Layered Docker Image Optimization

Since Spring Boot 2.3, the Spring-Boot-Layers-Index feature supports building layered Docker images, improving build speed and runtime performance.

5. Summary

Spring Boot Fat Jar’s ability to run directly stems from its unique nested JAR structure and custom class‑loading mechanism. By breaking the parent‑delegation model, providing a custom protocol handler, and configuring MANIFEST.MF, Spring Boot achieves self‑contained dependency loading and automatic embedded‑server startup, simplifying deployment and offering an efficient solution for microservices and standalone applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaMicroservicesSpring BootClass LoaderFat Jar
Cognitive Technology Team
Written by

Cognitive Technology Team

Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.