Deep Dive into Spring Boot Startup Process and Class Loading Mechanism
This article explores the inner workings of Spring Boot by dissecting a simple Hello World application, analyzing its startup sequence, class loading via FatJar, the custom JarLauncher, and automatic registration of RestControllers, while highlighting performance considerations and the complexity of its annotation-driven configuration.
Spring Boot is the most popular Java framework and makes Java applications easy to combine with Docker, simplifying microservice construction. However, its internals are complex. The author, a senior backend engineer with ten years of distributed high‑concurrency experience, analyzes the runtime process of a Spring Boot program to help readers understand its principles.
The article starts with a minimal Hello World example consisting of HelloController.java and Application.java. Running Application.java starts a simple RESTful web server.
// HelloController.java
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
// Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}When the server responds correctly, the author notes the surprising simplicity of the result despite Spring Boot’s reputation as a heavyweight monster. He then asks how HelloController is invoked without being referenced in Application 's main method.
Spring Boot scans the classpath for components annotated with @RestController. The @SpringBootApplication annotation includes @ComponentScan, which by default scans the current package. Detected controllers are automatically registered with the embedded Tomcat server.
The startup call stack is examined, showing that most of the depth belongs to Tomcat, while Spring Boot contributes fewer than ten layers. The author visualizes the stack with images (omitted here) and points out the relatively fast startup of a simple Hello World (about 5 seconds) compared to larger projects.
Spring Boot packages applications as Fat JARs: all dependencies are placed under BOOT-INF/lib and application classes under BOOT-INF/classes. The directory layout is displayed:
├── BOOT-INF
│ ├── classes
│ │ └── hello
│ └── lib
│ ├── classmate-1.3.4.jar
│ ├── hibernate-validator-6.0.12.Final.jar
│ ├── ... (other Spring and third‑party jars)
├── META-INF
│ ├── MANIFEST.MF
│ └── maven
│ └── org.springframework
└── org
└── springframework
└── bootThe manifest generated by the Spring Boot Maven plugin replaces the original Main-Class with org.springframework.boot.loader.JarLauncher and adds a Start-Class entry pointing to the user’s Application class.
// Generated by SpringBootLoader Plugin
Manifest-Version: 1.0
Implementation-Title: gs-spring-boot
Implementation-Version: 0.1.0
Built-By: qianwp
Implementation-Vendor-Id: org.springframework
Spring-Boot-Version: 2.0.5.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: hello.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/The JarLauncher creates a custom LaunchedURLClassLoader that loads classes from the embedded JARs. It registers the URL protocol handler, builds a classpath from the archives, and launches the user’s main class in a separate thread.
public class JarLauncher {
...
public static void main(String[] args) {
new JarLauncher().launch(args);
}
protected void launch(String[] args) {
JarFile.registerUrlProtocolHandler();
ClassLoader cl = createClassLoader(getClassPathArchives());
launch(args, getMainClass(), cl);
}
...
}The custom class loader ( LaunchedURLClassLoader) first delegates to the parent (the ExtensionClassLoader) for core Java classes, then searches the embedded JARs for application classes, caching package‑to‑JAR mappings to avoid repeated scans.
class LaunchedURLClassLoader extends URLClassLoader {
...
private Class<?> doLoadClass(String name) {
if (this.rootClassLoader != null) {
return this.rootClassLoader.loadClass(name);
}
findPackage(name);
Class<?> cls = findClass(name);
return cls;
}
...
}When loading a class, the loader constructs the class file path (e.g.,
jar:file:/workspace/springboot-demo/target/application.jar!/BOOT-INF/lib/snakeyaml-1.19.jar!/org/yaml/snakeyaml/Yaml.class) and searches each URL in its list.
Finally, the article revisits annotation processing: @SpringBootApplication includes @ComponentScan, which discovers @RestController classes. The @RestController meta‑annotation itself includes @Controller, causing Spring Boot to register the class as a URL handler in Tomcat.
@SpringBootApplication
public class Application { ... }
@ComponentScan
public @interface SpringBootApplication { ... }
@RestController
public @interface RestController { ... }The author concludes that, despite its complexity and performance quirks, Spring Boot’s annotation‑driven configuration and Fat‑Jar packaging make it indispensable in modern Java microservice development.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
High Availability Architecture
Official account for High Availability Architecture.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
