Unlock Tomcat’s Secrets: Deep Dive into Its Architecture, Design Patterns, and Class Loading
This article provides a comprehensive technical walkthrough of Tomcat, covering its connector and container architecture, lifecycle management, key design patterns such as Composite and Template Method, custom class‑loading mechanisms, hot‑reload process, and includes essential code snippets and diagrams for developers.
Understanding Tomcat Architecture
Tomcat is a mature Java HTTP server that implements the Servlet container. Its core responsibilities are split into Connectors (network I/O) and Containers (Servlet management).
Connector Components
Endpoint– low‑level socket listener (NIO, NIO2, APR). Processor – parses the HTTP request and creates Request / Response objects. Adapter (CoyoteAdapter) – converts Tomcat’s internal Request to a ServletRequest and invokes the container pipeline.
Container Hierarchy
Four container types form a tree: Engine → Host → Context → Wrapper. All implement the Container interface and inherit lifecycle management.
Lifecycle Management
Every container implements Lifecycle with the methods init(), start(), stop(), and destroy(). The abstract class LifecycleBase provides a template method that handles state transitions and fires LifecycleEvent listeners (Observer pattern).
Design Patterns Used
Composite – container tree.
Template Method – lifecycle handling and Tomcat’s init/start sequence.
Observer – lifecycle events.
Adapter – Connector → Container.
Chain of Responsibility – Pipeline/Valve processing.
Class Loading in Tomcat
Tomcat uses a custom WebAppClassLoader that breaks the standard parent‑delegation model: it first attempts to load a class from the web‑app’s /WEB-INF/classes and /WEB-INF/lib, then delegates to its parent. This allows each web application to have its own class space while still sharing common libraries via SharedClassLoader and CatalinaClassLoader.
Hot Reload
Each Context has its own class loader. The ContainerBackgroundProcessor runs periodically, detects changed class files, destroys the old Context, creates a new class loader, and re‑initialises the web application.
Request Flow
When a request arrives:
The appropriate Connector (e.g., HTTP/1.1 on port 8080) receives the socket data. Endpoint reads the bytes and hands them to a Processor.
The Processor builds a Tomcat Request object and passes it to the Adapter.
The Adapter converts the request to a ServletRequest and calls the container’s pipeline:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);The pipeline consists of a chain of Valve objects (Chain of Responsibility). The final valve invokes the target Wrapper, which creates the servlet instance and calls its service() method.
ProtocolHandler and Pipeline
The ProtocolHandler abstracts the network protocol. It composes Endpoint and Processor into a single component. Each container owns a Pipeline that holds a linked list of Valve objects. The first valve is invoked by the adapter, and each valve calls getNext().invoke() to continue the chain.
Mapper – Locating the Target Servlet
The Mapper component stores the mapping between host names, context paths, and servlet URL patterns. During request processing it resolves the request URL to a specific Wrapper (the servlet) by traversing this map.
JVM Class‑Loader Hierarchy
Standard JVM loaders: BootstrapClassLoader – loads core JRE classes (rt.jar). ExtClassLoader – loads JRE extensions ( $JAVA_HOME/jre/lib/ext). AppClassLoader – loads application classpath.
Tomcat adds: WebAppClassLoader – one instance per web application, providing isolation. SharedClassLoader – parent of each WebAppClassLoader, used for libraries shared across applications. CatalinaClassLoader (and optionally CommonClassLoader) – loads Tomcat’s own classes, keeping them separate from web apps.
WebAppClassLoader – Breaking Parent Delegation
Its loadClass() method first tries to find the class in the web app’s resources; if not found, it delegates to the parent. This prevents a web app from accidentally overriding JRE core classes while still allowing the app to load its own versions of libraries.
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 1. Check if already loaded by this loader
Class<?> c = findLoadedClass(name);
if (c == null) {
// 2. Try parent (ExtClassLoader) to protect JRE classes
try {
c = getJavaseClassLoader().loadClass(name);
} catch (ClassNotFoundException ignored) {}
}
if (c == null) {
// 3. Load from web‑app directory
c = findClass(name);
}
if (c == null) {
// 4. Fallback to system AppClassLoader
c = Class.forName(name, false, parent);
}
if (c == null) throw new ClassNotFoundException(name);
if (resolve) resolveClass(c);
return c;
}
}Embedded Tomcat Debugging Example
Embedded Tomcat debugging example: https://github.com/UniqueDong/tomcat-embedded
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
