Understanding Tomcat’s Class Loading Architecture
Introduction
Tomcat’s class loading mechanism is one of its core features that sets it apart from other web servers.In this article, we’ll dive deep into how Tomcat manages class loading,why it’s designed this way,and how to effectively work with it.
The Basics of Java ClassLoaders
Before diving into Tomcat’s specific implementation, let’s review the basics of java ClassLoader:
- Delegation Model: Java uses a parent-first delegation model
- Hierarchy:ClassLoaders are organized in a hierarchical structure
- Visibility:Child ClassLoaders can see classes loaded by parent ClassLoaders, but not vice versa
<span>public</span> <span>class</span> <span>BasicClassLoader</span> <span>extends</span> <span>ClassLoader</span> <span>{</span><span>@Override</span><span>protected</span> <span>Class</span><span><?></span> <span>loadClass</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>boolean</span> <span>resolve</span><span>)</span><span>throws</span> <span>ClassNotFoundException</span> <span>{</span><span>// First, check if the class has already been loaded</span><span>Class</span><span><?></span> <span>c</span> <span>=</span> <span>findLoadedClass</span><span>(</span><span>name</span><span>);</span><span>if</span> <span>(</span><span>c</span> <span>==</span> <span>null</span><span>)</span> <span>{</span><span>// If not loaded, delegate to parent</span><span>try</span> <span>{</span><span>if</span> <span>(</span><span>getParent</span><span>()</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span><span>c</span> <span>=</span> <span>getParent</span><span>().</span><span>loadClass</span><span>(</span><span>name</span><span>);</span><span>}</span><span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span><span>// If parent can't find it, try to find it locally</span><span>c</span> <span>=</span> <span>findClass</span><span>(</span><span>name</span><span>);</span><span>}</span><span>}</span><span>if</span> <span>(</span><span>resolve</span><span>)</span> <span>{</span><span>resolveClass</span><span>(</span><span>c</span><span>);</span><span>}</span><span>return</span> <span>c</span><span>;</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>BasicClassLoader</span> <span>extends</span> <span>ClassLoader</span> <span>{</span> <span>@Override</span> <span>protected</span> <span>Class</span><span><?></span> <span>loadClass</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>boolean</span> <span>resolve</span><span>)</span> <span>throws</span> <span>ClassNotFoundException</span> <span>{</span> <span>// First, check if the class has already been loaded</span> <span>Class</span><span><?></span> <span>c</span> <span>=</span> <span>findLoadedClass</span><span>(</span><span>name</span><span>);</span> <span>if</span> <span>(</span><span>c</span> <span>==</span> <span>null</span><span>)</span> <span>{</span> <span>// If not loaded, delegate to parent</span> <span>try</span> <span>{</span> <span>if</span> <span>(</span><span>getParent</span><span>()</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span> <span>c</span> <span>=</span> <span>getParent</span><span>().</span><span>loadClass</span><span>(</span><span>name</span><span>);</span> <span>}</span> <span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span> <span>// If parent can't find it, try to find it locally</span> <span>c</span> <span>=</span> <span>findClass</span><span>(</span><span>name</span><span>);</span> <span>}</span> <span>}</span> <span>if</span> <span>(</span><span>resolve</span><span>)</span> <span>{</span> <span>resolveClass</span><span>(</span><span>c</span><span>);</span> <span>}</span> <span>return</span> <span>c</span><span>;</span> <span>}</span> <span>}</span>public class BasicClassLoader extends ClassLoader { @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { // If not loaded, delegate to parent try { if (getParent() != null) { c = getParent().loadClass(name); } } catch (ClassNotFoundException e) { // If parent can't find it, try to find it locally c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } }
Enter fullscreen mode Exit fullscreen mode
Tomcat’s ClassLoader Hierarchy
Tomcat implements a sophisticated class loading architecture with multiple ClassLoaders:
- Bootstrap ClassLoader
- Loads Java core classes(rt.jar)
- Part of the JVM
- System ClassLoader
- Loads system classes from CLASSPATH
- Parent of Tomcat’s Common ClassLoader
- Common ClassLoader
- Loads classes shared across all web applications
- Located in $CATALINA_HOME/lib
- Catalina ClassLoader
- Loads Tomcat’s internal classes
- Isolated from web applications
- Shared ClassLoader
- Loads classes shared between web application
- Located in $CATALINA_BASE/shared/lib
- WebappClassLoader
- one per web application
- Loads classes from WEB_INF/classes and WEB-INF/lib Here’s a visualization of the hierarchy:
Bootstrap ClassLoader↑System ClassLoader↑Common ClassLoader↑ ↑Catalina Shared ClassLoader↑WebappClassLoaderBootstrap ClassLoader ↑ System ClassLoader ↑ Common ClassLoader ↑ ↑ Catalina Shared ClassLoader ↑ WebappClassLoaderBootstrap ClassLoader ↑ System ClassLoader ↑ Common ClassLoader ↑ ↑ Catalina Shared ClassLoader ↑ WebappClassLoader
Enter fullscreen mode Exit fullscreen mode
WebappClassLoader Implementation
The WebappClassLoader is particularly interesting as it breaks the normal parent-first delegation model:
<span>public</span> <span>class</span> <span>WebappClassLoader</span> <span>extends</span> <span>URLClassLoader</span> <span>{</span><span>@Override</span><span>public</span> <span>Class</span><span><?></span> <span>loadClass</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>boolean</span> <span>resolve</span><span>)</span><span>throws</span> <span>ClassNotFoundException</span> <span>{</span><span>// Check local cache first</span><span>Class</span><span><?></span> <span>clazz</span> <span>=</span> <span>findLoadedClass</span><span>(</span><span>name</span><span>);</span><span>if</span> <span>(</span><span>clazz</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span><span>return</span> <span>clazz</span><span>;</span><span>}</span><span>// Check security restrictions</span><span>checkPackageAccess</span><span>(</span><span>name</span><span>);</span><span>// Special handling for JavaEE classes</span><span>if</span> <span>(</span><span>name</span><span>.</span><span>startsWith</span><span>(</span><span>"javax."</span><span>))</span> <span>{</span><span>try</span> <span>{</span><span>clazz</span> <span>=</span> <span>getJavaEEClass</span><span>(</span><span>name</span><span>);</span><span>if</span> <span>(</span><span>clazz</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span><span>return</span> <span>clazz</span><span>;</span><span>}</span><span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span><span>// Continue with normal loading</span><span>}</span><span>}</span><span>// Try loading locally first</span><span>try</span> <span>{</span><span>clazz</span> <span>=</span> <span>findClass</span><span>(</span><span>name</span><span>);</span><span>if</span> <span>(</span><span>clazz</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span><span>return</span> <span>clazz</span><span>;</span><span>}</span><span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span><span>// Fall back to parent</span><span>}</span><span>// If not found locally, delegate to parent</span><span>return</span> <span>super</span><span>.</span><span>loadClass</span><span>(</span><span>name</span><span>,</span> <span>resolve</span><span>);</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>WebappClassLoader</span> <span>extends</span> <span>URLClassLoader</span> <span>{</span> <span>@Override</span> <span>public</span> <span>Class</span><span><?></span> <span>loadClass</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>boolean</span> <span>resolve</span><span>)</span> <span>throws</span> <span>ClassNotFoundException</span> <span>{</span> <span>// Check local cache first</span> <span>Class</span><span><?></span> <span>clazz</span> <span>=</span> <span>findLoadedClass</span><span>(</span><span>name</span><span>);</span> <span>if</span> <span>(</span><span>clazz</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span> <span>return</span> <span>clazz</span><span>;</span> <span>}</span> <span>// Check security restrictions</span> <span>checkPackageAccess</span><span>(</span><span>name</span><span>);</span> <span>// Special handling for JavaEE classes</span> <span>if</span> <span>(</span><span>name</span><span>.</span><span>startsWith</span><span>(</span><span>"javax."</span><span>))</span> <span>{</span> <span>try</span> <span>{</span> <span>clazz</span> <span>=</span> <span>getJavaEEClass</span><span>(</span><span>name</span><span>);</span> <span>if</span> <span>(</span><span>clazz</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span> <span>return</span> <span>clazz</span><span>;</span> <span>}</span> <span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span> <span>// Continue with normal loading</span> <span>}</span> <span>}</span> <span>// Try loading locally first</span> <span>try</span> <span>{</span> <span>clazz</span> <span>=</span> <span>findClass</span><span>(</span><span>name</span><span>);</span> <span>if</span> <span>(</span><span>clazz</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span> <span>return</span> <span>clazz</span><span>;</span> <span>}</span> <span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span> <span>// Fall back to parent</span> <span>}</span> <span>// If not found locally, delegate to parent</span> <span>return</span> <span>super</span><span>.</span><span>loadClass</span><span>(</span><span>name</span><span>,</span> <span>resolve</span><span>);</span> <span>}</span> <span>}</span>public class WebappClassLoader extends URLClassLoader { @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // Check local cache first Class<?> clazz = findLoadedClass(name); if (clazz != null) { return clazz; } // Check security restrictions checkPackageAccess(name); // Special handling for JavaEE classes if (name.startsWith("javax.")) { try { clazz = getJavaEEClass(name); if (clazz != null) { return clazz; } } catch (ClassNotFoundException e) { // Continue with normal loading } } // Try loading locally first try { clazz = findClass(name); if (clazz != null) { return clazz; } } catch (ClassNotFoundException e) { // Fall back to parent } // If not found locally, delegate to parent return super.loadClass(name, resolve); } }
Enter fullscreen mode Exit fullscreen mode
Key Features and Benefits
- Isolation
- Each web application has its own ClassLoader
- Application can use different version of the same library
- Resource Management
- Efficient class unloading when application are stopped
- Prevent memory leaks
- Security
- Class loading restrictions
- Package access control
Common Issues and Solutions
- ClassNotFoundException
<span>// Common cause: Missing dependencies</span><span>try</span> <span>{</span><span>Class</span><span>.</span><span>forName</span><span>(</span><span>"com.example.MyClass"</span><span>);</span><span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span><span>// Handle the error</span><span>logger</span><span>.</span><span>error</span><span>(</span><span>"Failed to load class"</span><span>,</span> <span>e</span><span>);</span><span>}</span><span>// Common cause: Missing dependencies</span> <span>try</span> <span>{</span> <span>Class</span><span>.</span><span>forName</span><span>(</span><span>"com.example.MyClass"</span><span>);</span> <span>}</span> <span>catch</span> <span>(</span><span>ClassNotFoundException</span> <span>e</span><span>)</span> <span>{</span> <span>// Handle the error</span> <span>logger</span><span>.</span><span>error</span><span>(</span><span>"Failed to load class"</span><span>,</span> <span>e</span><span>);</span> <span>}</span>// Common cause: Missing dependencies try { Class.forName("com.example.MyClass"); } catch (ClassNotFoundException e) { // Handle the error logger.error("Failed to load class", e); }
Enter fullscreen mode Exit fullscreen mode
- NoClassDefFoundError
<span>// Solution: Ensure all dependencies are in the correct location</span><span>// WEB-INF/lib for application-specific libraries</span><span>// $CATALINA_HOME/lib for shared libraries</span><span>// Solution: Ensure all dependencies are in the correct location</span> <span>// WEB-INF/lib for application-specific libraries</span> <span>// $CATALINA_HOME/lib for shared libraries</span>// Solution: Ensure all dependencies are in the correct location // WEB-INF/lib for application-specific libraries // $CATALINA_HOME/lib for shared libraries
Enter fullscreen mode Exit fullscreen mode
- Multiple Versions of Libraries
<span><!-- In web.xml, you can delegate loading to parent --></span><span><Context></span><span><Loader</span> <span>delegate=</span><span>"true"</span><span>/></span><span></Context></span><span><!-- In web.xml, you can delegate loading to parent --></span> <span><Context></span> <span><Loader</span> <span>delegate=</span><span>"true"</span><span>/></span> <span></Context></span><!-- In web.xml, you can delegate loading to parent --> <Context> <Loader delegate="true"/> </Context>
Enter fullscreen mode Exit fullscreen mode
Best Practice
- Dependency Management
- Keep application-specific libraries in WEB-INF/lib
- Place shared libraries in common locations
- Class Loading Configuration
<span><</span><span>!-- catalina.properties --> </span><span>common.loader</span><span>=</span><span>${catalina.base}/lib,${catalina.base}/lib/*.jar</span><span>shared.loader</span><span>=</span><span>server.loader</span><span>=</span><span><</span><span>!-- catalina.properties --> </span><span>common.loader</span><span>=</span><span>${catalina.base}/lib,${catalina.base}/lib/*.jar</span> <span>shared.loader</span><span>=</span> <span>server.loader</span><span>=</span><!-- catalina.properties --> common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar shared.loader= server.loader=
Enter fullscreen mode Exit fullscreen mode
- Monitoring and Debugging
<span>// Enable class loader logging</span><span>System</span><span>.</span><span>setProperty</span><span>(</span><span>"java.security.debug"</span><span>,</span> <span>"loader"</span><span>);</span><span>// Enable class loader logging</span> <span>System</span><span>.</span><span>setProperty</span><span>(</span><span>"java.security.debug"</span><span>,</span> <span>"loader"</span><span>);</span>// Enable class loader logging System.setProperty("java.security.debug", "loader");
Enter fullscreen mode Exit fullscreen mode
Conclusion
Understanding Tomcat’s class loading architecture is crucial for:
- Troubleshooting class loading issues
- Proper application deployment
- Optimal performance and resource usage
- Maintaining application isolation By following the best practices and understanding the hierarchy, you can avoid common pitfalls and ensure your applications run smoothly in Tomcat.
References
- Apache Tomcat Documentation
- Java ClassLoader Specification
- Java EE Specification This comprehensive guide should help you understand and work effectively with Tomcat’s class loading architecture. Remember that class loading is a complex topic, and it’s worth investing time to understand it thoroughly for successful Java web application development.
原文链接:Tomcat Architecture Series-6.Understanding Tomcat’s Class Loading Architecture
暂无评论内容