Tomcat 的类加载机制设计得相当精巧,旨在支持 Web 应用的隔离性以及 Servlet 规范的要求。它采用了与标准 Java 类加载器不同的层次结构,以确保每个 Web 应用程序都能拥有自己的类库版本,同时也能共享 Tomcat 自身及公共库中的类。以下是 Tomcat 类加载机制的核心要点:
1. 类加载器层次结构
Tomcat 的类加载器遵循双亲委派模型(Parent Delegation Model),但为了满足 Web 应用程序之间的隔离需求,它在标准 Java 类加载器的基础上做了扩展和调整。Tomcat 的类加载器大致可以分为以下几个层次:
-
Bootstrap ClassLoader:负责加载 JVM 核心类库(如
java.lang.*等)。 -
Extension ClassLoader:负责加载位于
$JAVA_HOME/lib/ext目录下的扩展类库。 -
System ClassLoader:也称为 Application ClassLoader,负责加载应用类路径上的类(通过
-classpath或-cp参数指定)。 -
***mon ClassLoader:Tomcat 共享类加载器,加载对所有应用及 Tomcat 自身可见的类库。这些类通常位于
$CATALINA_HOME/lib目录下。 - Webapp ClassLoader:为每个 Web 应用单独创建的一个类加载器实例,用于加载该应用特有的类库(如 WEB-INF/classes 和 WEB-INF/lib 下的 JAR 文件)。这是打破双亲委派模型的关键所在,允许应用程序优先于父类加载器加载自己的类。
2. 打破双亲委派模型
传统的双亲委派模型要求子类加载器请求加载一个类时首先委托给其父类加载器尝试加载。然而,在 Tomcat 中,为了实现 Web 应用间的类隔离,Webapp ClassLoader 被设计成首先尝试自己加载类(即从当前应用的类路径中加载),如果找不到再委托给父类加载器。这种方式确保了不同 Web 应用可以使用各自版本的相同库而不会相互冲突。
3. 类加载顺序
当一个 Web 应用需要加载某个类时,Tomcat 遵循以下顺序尝试加载:
- 使用 Webapp ClassLoader 尝试从
/WEB-INF/classes加载类。 - 如果未找到,则尝试从
/WEB-INF/lib/*.jar中加载。 - 如果仍未找到,则向上委托给 ***mon ClassLoader 进行查找。
- 最后,如果 ***mon ClassLoader 也无法找到,则继续向上委托给 System ClassLoader、Extension ClassLoader 和 Bootstrap ClassLoader。
这种机制保证了每个 Web 应用都可以拥有独立的类库集合,并且可以在不影响其他应用的情况下更新或替换这些库。
4. 配置与优化
Tomcat 提供了一些配置选项来调整类加载行为,例如可以通过修改 catalina.properties 文件中的 ***mon.loader, server.loader, shared.loader 属性来定制 ***mon ClassLoader 的搜索路径。此外,合理设置这些路径可以帮助提高性能并减少内存占用。
总之,Tomcat 的类加载机制通过精心设计的类加载器层次结构和适当的类加载策略,实现了 Web 应用程序之间的有效隔离,同时也提供了足够的灵活性以便于开发人员根据具体需求进行配置和优化。