← Back to list

NTTData second interview: Why can Spring Boot JARs run directly?

Why Can Spring Boot JARs Run Directly? The Question That Ended a NTT Data Interview

Umesh Kumar Yadav in Stackademic · 2026-07-10 03:44 · 11 claps · 6.6 min read paywalled
#java #spring-boot #software-development #software-engineering #programming
Open on Medium ↗
Wiki topics: 💻 · Programming

NTTData second interview: Why can Spring Boot JARs run directly? I said it’s because it has an embedded Tomcat container, and he told me to turn left and leave.

AI image

AI image

Why Can Spring Boot JARs Run Directly? The Question That Ended a NTT Data Interview

“Why can Spring Boot JARs run directly?”

During a Ntt Data interview, I confidently replied:

“Because Spring Boot has an embedded Tomcat container.”

The interviewer smiled and said:

“Turn left and leave.”

While the answer wasn’t completely wrong, it barely scratched the surface.

The embedded Tomcat is only one piece of a much larger architecture. The real magic lies in how Spring Boot packages applications, bootstraps the JVM, loads dependencies, and configures itself at runtime.

In this article, we’ll dive deep into what actually happens when you execute:

java -jar my-application.jar

By the end, you’ll understand exactly why Spring Boot applications can run as standalone executables without installing Tomcat or configuring complex classpaths.

The Traditional Java Deployment Model

Before Spring Boot, deploying a Java web application involved several manual steps.

A typical workflow looked like this:

Compile Code
      │
      ▼
Package as WAR
      │
      ▼
Install Apache Tomcat
      │
      ▼
Copy WAR into webapps/
      │
      ▼
Configure Server
      │
      ▼
Start Tomcat

Developers had to worry about:

  • Installing the correct server version
  • Managing dependency conflicts
  • Setting classpaths
  • Server configuration
  • Environment consistency

A missing JAR could easily result in:

java.lang.ClassNotFoundException

or

NoClassDefFoundError

Deployment was often more difficult than development itself.

Spring Boot Changed Everything

Spring Boot introduced a radically simpler deployment model.

Instead of deploying a WAR into Tomcat, you simply package your application as an executable JAR.

mvn clean package

java -jar application.jar

That’s it.

No external server.

No manual classpath.

No deployment directory.

Just a single executable file.

But how?

The Secret Begins with the Fat JAR

Spring Boot creates what’s called a Fat JAR (also known as an Uber JAR).

Unlike a normal JAR, it contains:

  • Your application classes
  • Spring Framework
  • Spring Boot
  • Tomcat/Jetty/Undertow
  • Third-party libraries
  • Resources
  • Metadata

Everything needed to run the application exists inside one file.

Think of it like shipping an entire apartment instead of asking someone to assemble furniture after delivery.

Normal JAR vs Fat JAR

Packaging Process

Spring Boot relies on the Spring Boot Maven Plugin (or Gradle plugin).

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

During packaging:

mvn package

the plugin performs an additional step:

repackage

Instead of generating a normal JAR, it reorganizes the archive into a Spring Boot executable JAR.

What’s Inside a Spring Boot JAR?

If you unzip a Spring Boot application, you’ll see something like this:

application.jar
│
├── META-INF
│      └── MANIFEST.MF
│
├── BOOT-INF
│      ├── classes
│      └── lib
│
└── org
       └── springframework
             └── boot
                  └── loader

Let’s understand each part.

META-INF/

Contains metadata.

Most importantly:

MANIFEST.MF

BOOT-INF/classes

Contains:

  • Your compiled classes
  • application.properties
  • static files
  • templates
  • resources

Essentially everything belonging to your project.

BOOT-INF/lib

Contains every dependency.

Example:

spring-context.jar

spring-web.jar

spring-core.jar

tomcat-embed-core.jar

jackson-databind.jar

hibernate-core.jar

No external Maven repository is required at runtime.

org/springframework/boot/loader

This folder contains Spring Boot’s custom launcher classes.

These classes make executable JARs possible.

The MANIFEST.MF File

A normal executable JAR contains something like:

Main-Class: com.example.Application

Spring Boot is different.

Example:

Main-Class:
org.springframework.boot.loader.launch.JarLauncher

Start-Class:
com.example.Application

Notice something interesting?

Your application is not the Main-Class.

Instead,

JarLauncher

starts first.

Why?

Because Java cannot load nested JAR files by default.

Spring Boot solves this problem with a custom launcher.

What Happens When You Execute java -jar?

When you run

java -jar application.jar

the JVM performs these steps.

JVM
 │
 ▼
Read MANIFEST.MF
 │
 ▼
Load JarLauncher
 │
 ▼
Create custom ClassLoader
 │
 ▼
Load BOOT-INF/lib/*
 │
 ▼
Load BOOT-INF/classes
 │
 ▼
Find Start-Class
 │
 ▼
Invoke main()

Everything starts from JarLauncher.

Why Doesn’t the JVM Load Nested JARs?

Imagine this structure:

application.jar

├── lib
        ├── spring.jar
        ├── jackson.jar
        └── mysql.jar

The standard JVM class loader cannot directly read JAR files inside another JAR.

It only understands:

Directory

or
Standalone JAR

Spring Boot therefore provides its own class loader.

Spring Boot’s Custom ClassLoader

Spring Boot creates a specialized class loader that:

  • Reads nested JARs
  • Builds the classpath dynamically
  • Loads application classes
  • Loads dependencies
  • Starts the real application

This is one of the biggest reasons executable Spring Boot JARs work.

Without this loader, nested dependencies would be invisible to the JVM.

The Startup Flow

The startup process looks like this:

java -jar
      │
      ▼
JarLauncher
      │
      ▼
Create LaunchedURLClassLoader
      │
      ▼
Read BOOT-INF/lib
      │
      ▼
Load Dependencies
      │
      ▼
Read Start-Class
      │
      ▼
Invoke SpringApplication.run()

Only after this process does your main() method execute.

Embedded Web Server

Now comes the part most developers already know.

When you include:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Boot automatically adds:

  • Embedded Tomcat (default)
  • or Jetty
  • or Undertow

During startup:

Spring Boot
      │
      ▼
Create Tomcat Instance
      │
      ▼
Configure Connectors
      │
      ▼
Register DispatcherServlet
      │
      ▼
Listen on Port 8080

No separate Tomcat installation is necessary.

Your application already contains one.

Is Embedded Tomcat the Real Reason?

No.

This is where many interview answers go wrong.

Embedded Tomcat explains how HTTP requests are served, but it doesn’t explain how the executable JAR starts.

The complete answer includes:

  • Fat JAR packaging
  • Custom launcher
  • Custom class loader
  • MANIFEST.MF
  • Nested dependency loading
  • Auto-configuration
  • Embedded servlet container

Mentioning only Tomcat misses most of the architecture.

Auto Configuration

Once the launcher hands control to your application, Spring Boot initializes the framework.

Your application starts with:

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@SpringBootApplication is actually a combination of three annotations:

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan

Each serves a different purpose.

1. @SpringBootConfiguration

Marks the class as a Spring configuration class.

Equivalent to:

@Configuration

2. @ComponentScan

Automatically discovers:

  • Controllers
  • Services
  • Repositories
  • Components

Example:

@Service
public class UserService {

}

No XML configuration required.

3. @EnableAutoConfiguration

This is the real magic.

Spring Boot examines:

  • Available libraries
  • Existing beans
  • Environment
  • Properties

Then configures everything automatically.

Examples:

If Spring MVC exists →

Configure DispatcherServlet.

If Tomcat exists →

Configure embedded server.

If HikariCP exists →

Configure DataSource.

If Jackson exists →

Configure ObjectMapper.

All without writing configuration code.

Auto-Configuration in Spring Boot 3

Older Spring Boot versions loaded auto-configurations from:

META-INF/spring.factories

Spring Boot 3 introduced a new mechanism:

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

This improves startup performance and simplifies auto-configuration registration.

Why Doesn’t Spring Configure Everything?

Spring Boot uses conditional annotations such as:

@ConditionalOnClass

@ConditionalOnMissingBean

@ConditionalOnProperty

@ConditionalOnWebApplication

Example:

@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {

}

If DataSource isn't present, this configuration is skipped.

This keeps startup lightweight and avoids unnecessary bean creation.

Complete Startup Sequence

Putting everything together, the full process looks like this:

mvn package
        │
        ▼
Spring Boot Maven Plugin
        │
        ▼
Generate Fat JAR
        │
        ▼
java -jar
        │
        ▼
JVM reads MANIFEST.MF
        │
        ▼
JarLauncher
        │
        ▼
Custom ClassLoader
        │
        ▼
Load BOOT-INF/lib
        │
        ▼
Load BOOT-INF/classes
        │
        ▼
Find Start-Class
        │
        ▼
SpringApplication.run()
        │
        ▼
Component Scan
        │
        ▼
Auto Configuration
        │
        ▼
Start Embedded Tomcat
        │
        ▼
Application Ready

Interview Answer (2-Minute Version)

If an interviewer asks:

Why can Spring Boot JARs run directly?

A strong answer would be:

Spring Boot packages the application as a self-contained Fat JAR using the Spring Boot Maven or Gradle plugin.

During packaging, it modifies the MANIFEST.MF file so the JVM starts JarLauncher instead of the application's main class.

*JarLauncher creates a custom class loader that can load nested JARs from BOOT-INF/lib and application classes from BOOT-INF/classes, something the default JVM class loader cannot do.*

It then invokes the actual Start-Class, where SpringApplication.run() initializes the Spring context, performs component scanning, applies auto-configuration, and starts the embedded servlet container such as Tomcat.

This architecture allows a Spring Boot application to run with a simple java -jar command without requiring an external application server.

Key Takeaways

  • Spring Boot packages applications as a Fat (Uber) JAR containing both application code and dependencies.
  • The Spring Boot Maven/Gradle plugin repackages the archive into an executable format.
  • The MANIFEST.MF file points to JarLauncher, not your application class.
  • JarLauncher creates a custom class loader capable of loading nested JARs from BOOT-INF/lib.
  • Application classes are loaded from BOOT-INF/classes, and the real Start-Class is invoked afterward.
  • Embedded Tomcat, Jetty, or Undertow eliminates the need for an external servlet container.
  • @SpringBootApplication combines configuration, component scanning, and auto-configuration to bootstrap the application.
  • Since Spring Boot 3, auto-configurations are discovered through AutoConfiguration.imports instead of spring.factories.

Final Thoughts

The embedded Tomcat is only the visible part of the iceberg. The real innovation behind Spring Boot’s executable JARs is the combination of custom packaging, a specialized launcher, a custom class-loading mechanism, nested dependency management, and intelligent auto-configuration.

Understanding these internals not only helps you answer interview questions with confidence but also gives you a much deeper appreciation of how Spring Boot simplifies Java application deployment while hiding a remarkable amount of engineering beneath a single command:

java -jar application.jar

That’s far more than “because it has embedded Tomcat.” It’s an elegant startup architecture that transformed Java deployment.

Thank you for reading!

If you found this article useful, feel free to give it a clap 👏, share it with your friends, and follow for more deep dives into distributed systems, Spring Boot architecture, Kafka, Redis, and high-scale backend engineering.

😊 Your support is the biggest motivation to continue sharing technical insights.

Before you go

  • Please take a moment to like the post and follow the writer!
  • Did you know that over 400,000 developers share what they’re building, learning, and discovering across our platforms every month? Learn how you can contribute here

메타데이터
post_id
b3e09492a162
slug
nttdata-second-interview-why-can-spring-boot-jars-run-directly-b3e09492a162
url
https://blog.stackademic.com/nttdata-second-interview-why-can-spring-boot-jars-run-directly-b3e09492a162
canonical_url
https://blog.stackademic.com/nttdata-second-interview-why-can-spring-boot-jars-run-directly-b3e09492a162
author_url
https://medium.com/@umeshcapg
status
ok
fetched_at
2026-07-11 00:45:12