Backend Development 21 min read

Understanding Spring Boot Executable JAR, JarLauncher, and Custom Class Loading

This article explains how Spring Boot's spring-boot-maven-plugin creates an executable JAR, describes the internal structure and Manifest, details the role of JarLauncher and the custom LaunchedURLClassLoader, and provides concrete code examples for class loading and execution.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Understanding Spring Boot Executable JAR, JarLauncher, and Custom Class Loading

Spring Boot provides the spring-boot-maven-plugin to package an application as an executable JAR.

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

The generated JAR contains a META-INF directory, a lib folder with third‑party dependencies, Spring Boot loader classes, and the module’s own classes.

├── META-INF
│   └── MANIFEST.MF
├── lib
│   ├── aopalliance-1.0.jar
│   ├── classmate-1.1.0.jar
│   ├── spring-boot-1.3.5.RELEASE.jar
│   └── ...
├── org
│   └── springframework
│       └── boot
│           └── loader
│               └── ExecutableArchiveLauncher$1.class
└── spring
    └── study
        └── executablejar
            └── ExecutableJarApplication.class

Running java -jar executable-jar-1.0-SNAPSHOT.jar starts the program.

The JAR includes four types of entries:

META-INF folder (contains MANIFEST.MF describing the JAR).

lib directory with third‑party JARs.

Spring Boot loader related code.

The application’s own classes.

The MANIFEST.MF defines the main class as org.springframework.boot.loader.JarLauncher :

Manifest-Version: 1.0
Implementation-Title: executable-jar
Implementation-Version: 1.0-SNAPSHOT
Start-Class: spring.study.executablejar.ExecutableJarApplication
Main-Class: org.springframework.boot.loader.JarLauncher

When the JAR is executed, the JarLauncher (a subclass of ExecutableArchiveLauncher ) is invoked. Its main method simply creates a new instance and calls launch(args) :

public static void main(String[] args) {
    new JarLauncher().launch(args);
}

The launch method registers the custom URL protocol handler, builds a class loader from the nested JARs, obtains the Start-Class from the manifest, and delegates to the overloaded launch method:

protected void launch(String[] args) throws Exception {
    JarFile.registerUrlProtocolHandler();
    ClassLoader classLoader = createClassLoader(getClassPathArchives());
    launch(args, getMainClass(), classLoader);
}

getMainClass() reads the Start-Class attribute from the manifest:

public String getMainClass() throws Exception {
    Manifest manifest = getManifest();
    String mainClass = null;
    if (manifest != null) {
        mainClass = manifest.getMainAttributes().getValue("Start-Class");
    }
    if (mainClass == null) {
        throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
    }
    return mainClass;
}

The overloaded launch creates a MainMethodRunner , starts a new thread, and invokes the main method of the start class via reflection:

protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
    Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
    Thread runnerThread = new Thread(runner);
    runnerThread.setContextClassLoader(classLoader);
    runnerThread.setName(Thread.currentThread().getName());
    runnerThread.start();
}

The MainMethodRunner implementation:

@Override
public void run() {
    try {
        Class mainClass = Thread.currentThread().getContextClassLoader()
            .loadClass(this.mainClassName);
        Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
        if (mainMethod == null) {
            throw new IllegalStateException(this.mainClassName + " does not have a main method");
        }
        mainMethod.invoke(null, this.args);
    } catch (Exception ex) {
        UncaughtExceptionHandler handler = Thread.currentThread().getUncaughtExceptionHandler();
        if (handler != null) {
            handler.uncaughtException(Thread.currentThread(), ex);
        }
        throw new RuntimeException(ex);
    }
}

Spring Boot’s custom class loader LaunchedURLClassLoader overrides loadClass to try the root loader, then locate the class in the nested JARs, and finally delegate to the parent:

private Class doLoadClass(String name) throws ClassNotFoundException {
    try {
        if (this.rootClassLoader != null) {
            return this.rootClassLoader.loadClass(name);
        }
    } catch (Exception ex) {
        // ignore
    }
    try {
        findPackage(name);
        Class cls = findClass(name);
        return cls;
    } catch (Exception ex) {
        // ignore
    }
    return super.loadClass(name, false);
}

The findClass method resolves the class name to a resource path, searches the URLs of the nested JARs, and defines the class if found:

protected Class findClass(final String name) throws ClassNotFoundException {
    try {
        return AccessController.doPrivileged(new PrivilegedExceptionAction
() {
            public Class run() throws ClassNotFoundException {
                String path = name.replace('.', '/').concat(".class");
                Resource res = ucp.getResource(path, false);
                if (res != null) {
                    try {
                        return defineClass(name, res);
                    } catch (IOException e) {
                        throw new ClassNotFoundException(name, e);
                    }
                } else {
                    throw new ClassNotFoundException(name);
                }
            }
        }, acc);
    } catch (PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
}

A test registers the custom URL protocol handler and creates a LaunchedURLClassLoader with URLs that use the jar:file:...!/ syntax, then loads classes such as org.springframework.boot.loader.JarLauncher , org.springframework.boot.SpringApplication , and a Spring MVC auto‑configuration class:

JarFile.registerUrlProtocolHandler();
LaunchedURLClassLoader classLoader = new LaunchedURLClassLoader(
    new URL[]{
        new URL("jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/"),
        new URL("jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-1.3.5.RELEASE.jar!/")
    },
    LaunchedURLClassLoaderTest.class.getClassLoader()
);
classLoader.loadClass("org.springframework.boot.loader.JarLauncher");
classLoader.loadClass("org.springframework.boot.SpringApplication");
classLoader.loadClass("org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration");

In summary, the article details how Spring Boot builds an executable JAR, the purpose of JarLauncher , the custom class‑loading strategy employed by Spring Boot Loader, and provides concrete code snippets illustrating each step.

JavaMavenSpring BootClassLoaderExecutable JARJarLauncher
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

login 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.