Tomcat Architecture Series-6.Understanding Tomcat’s Class Loading Architecture

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:

  1. Delegation Model: Java uses a parent-first delegation model
  2. Hierarchy:ClassLoaders are organized in a hierarchical structure
  3. 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:

  1. Bootstrap ClassLoader
    • Loads Java core classes(rt.jar)
    • Part of the JVM
  2. System ClassLoader
    • Loads system classes from CLASSPATH
    • Parent of Tomcat’s Common ClassLoader
  3. Common ClassLoader
    • Loads classes shared across all web applications
    • Located in $CATALINA_HOME/lib
  4. Catalina ClassLoader
    • Loads Tomcat’s internal classes
    • Isolated from web applications
  5. Shared ClassLoader
    • Loads classes shared between web application
    • Located in $CATALINA_BASE/shared/lib
  6. 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
WebappClassLoader
Bootstrap ClassLoader
       ↑
System ClassLoader
       ↑
Common ClassLoader
    ↑        ↑
Catalina   Shared ClassLoader
            ↑
        WebappClassLoader
Bootstrap 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

  1. Isolation
    • Each web application has its own ClassLoader
    • Application can use different version of the same library
  2. Resource Management
    • Efficient class unloading when application are stopped
    • Prevent memory leaks
  3. Security
    • Class loading restrictions
    • Package access control

Common Issues and Solutions

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

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

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

  1. Dependency Management
    • Keep application-specific libraries in WEB-INF/lib
    • Place shared libraries in common locations
  2. 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

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

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
Work hard in silence, let success make the noise.
在沉默中努力,让成功自己发声
评论 抢沙发

请登录后发表评论

    暂无评论内容