概述

与许多服务器应用程序一样,Tomcat 安装了各种类加载器(即实现 java.lang.ClassLoader 的类), 以允许容器的不同部分以及容器上运行的 Web 应用程序访问可用类和资源的不同存储库。 此机制用于提供 Servlet 规范版本 2.4 中定义的功能,特别是第 9.4 节和第 9.6 节。

在 Java 环境中,类加载器排列在父子树中。 通常,当要求类加载器加载特定类或资源时,它会首先将请求委托给父类加载器, 然后仅在父类加载器找不到请求的类或资源时才在自己的存储库中查找。 请注意,Web 应用程序类加载器的模型与此略有不同,如下所述,但主要原则是相同的。

当 Tomcat 启动时,它会创建一组类加载器,这些加载器被组织为以下父子关系,其中父类加载器位于子类加载器之上:

       启动
         |
       系统
         |
       常见
       /    \
  Webapp1 Webapp2 ...

下一节将详细讨论每个类加载器的特征,包括它们使可见的类的来源和资源。

类加载器定义

如上图所示,Tomcat 在初始化时创建以下类加载器:

  • Bootstrap — 此类加载器包含 Java 虚拟机提供的基本运行时类,以及 System Extensions 目录 ($JAVA_HOME/jre/lib/ext) 中存在的 JAR 文件中的任何类。注意:某些 JVM 可能将其实现为多个类加载器,或者它可能根本不可见(作为类加载器)。

  • System—此类加载器通常根据 CLASSPATH 环境变量的内容进行初始化。所有这些类对 Tomcat 内部类和 Web 应用程序都是可见的。但是,标准 Tomcat 启动脚本($CATALINA_HOME/bin/catalina.sh%CATALINA_HOME%\bin\catalina.bat)完全忽略了 CLASSPATH 环境变量本身的内容,而是从以下存储库构建 System 类加载器:

    • $CATALINA_HOME/bin/bootstrap.jar — 包含用于初始化 Tomcat 服务器的 main() 方法,以及它所依赖的类加载器实现类。

    • $CATALINA_BASE/bin/tomcat-juli.jar$CATALINA_HOME/bin/tomcat-juli.jar — 记录实现类。其中包括 java.util.logging API 的增强类(称为 Tomcat JULI)和 Tomcat 内部使用的 Apache Commons Logging 库的包重命名副本。有关更多详细信息,请参阅日志记录文档。如果 $CATALINA_BASE/bin 中存在 tomcat-juli.jar,则使用它而不是 $CATALINA_HOME/bin 中的那个。它在某些日志记录配置中很有用

    • $CATALINA_HOME/bin/commons-daemon.jar — Apache Commons Daemon 项目中的类。此 JAR 文件不存在于 CLASSPATH`路径的 `catalina.bat|.sh 脚本中,但从 bootstrap.jar 的清单文件中引用。

  • Common — 此类加载器包含对 Tomcat 内部类和所有 Web 应用程序可见的其他类。 通常,应用程序类应该放在这里。此类加载器搜索的位置由 $CATALINA_BASE/conf/catalina.properties 中的 common.loader 属性定义。默认设置将按列出的顺序搜索以下位置:

    • $CATALINA_BASE/lib 中解压缩的类和资源

    • $CATALINA_BASE/lib 中的 JAR 文件

    • $CATALINA_HOME/lib 中解压缩的类和资源

    • $CATALINA_HOME/lib 中的 JAR 文件

默认情况下,这包括以下内容:

  • annotations-api.jar — Jakarta Annotations 3.0 类。

  • catalina.jar — Tomcat 的 Catalina servlet 容器部分的实现。

  • catalina-ant.jar — 可选。Tomcat Catalina Ant 任务,用于使用 Manager Web 应用程序。

  • catalina-ha.jar — 可选。高可用性软件包,提供基于 Tribes 构建的会话集群功能。

  • catalina-ssi.jar — 可选。服务器端 Includes 模块。

  • catalina-storeconfig.jar — 可选。从当前状态生成 XML 配置文件。

  • catalina-tribes.jar — 可选。高可用性包使用的组通信包。

  • ecj-*.jar — 可选。用于将 JSP 编译为 Servlet 的 Eclipse JDT Java 编译器。

  • el-api.jar — 可选。EL 6.0 API 的 API 中。

  • jakartaee-migration-*-shaded.jar — 可选。提供从 Java EE 8 到 Jakarta EE 9 的 Web 应用程序转换。

  • jasper.jar — 可选。Tomcat Jasper JSP 编译器和运行时。

  • jasper-el.jar — 可选。Tomcat EL 实现。

  • jaspic-api.jar — Jakarta Authentication 3.1 API.

  • jsp-api.jar — 可选。Jakarta Pages 4.0 API。

  • servlet-api.jar — Jakarta Servlet 6.1 API.

  • tomcat-api.jar – Tomcat 定义的多个接口。

  • tomcat-coyote.jar — Tomcat 连接器和实用程序类。

  • tomcat-dbcp.jar — 可选。基于 Apache Commons Pool 2 和 Apache Commons DBCP 2 的包重命名副本的数据库连接池实现。

  • tomcat-i18n-**.jar — 包含其他语言的资源包的可选 JAR。由于默认捆绑包也包含在每个单独的 JAR 中,因此如果不需要消息国际化,则可以安全地删除它们。

  • tomcat-jdbc.jar — 可选。另一种数据库连接池实现,称为 Tomcat JDBC 池。有关更多详细信息,请参阅文档。

  • tomcat-jni.jar — 提供与 Tomcat Native 库的集成。

  • tomcat-util.jar – Apache Tomcat 的各种组件使用的通用类。

  • tomcat-util-scan.jar – 提供 Tomcat 使用的类扫描功能。

  • tomcat-websocket.jar — 可选。Jakarta WebSocket 2.2 实现

  • websocket-api.jar — 可选。Jakarta WebSocket 2.2 API

  • websocket-client-api.jar — 可选。Jakarta WebSocket 2.2 客户端 API

    • WebappX — 为部署在单个 Tomcat 实例中的每个 Web 应用程序创建一个类加载器。Web 应用程序的 /WEB-INF/classes 目录中的所有解压缩的类和资源,以及 Web 应用程序的 /WEB-INF/lib 目录下的 JAR 文件中的类和资源,都对此 Web 应用程序可见,但对其他 Web 应用程序不可见。

如上所述,Web 应用程序类加载器与默认的 Java 委托模型不同(根据 Servlet 规范版本 2.4 第 9.7.2 节 Web 应用程序类加载器中的建议)。当处理从 Web 应用程序的 WebappX 类加载器加载类的请求时,该类加载器将首先在本地存储库中查找,而不是在查看之前进行委派。也有例外。作为 JRE 基类一部分的类不能被覆盖。有一些例外情况,例如 XML 解析器组件,可以使用可升级模块功能覆盖这些组件。最后,Web 应用程序类加载器将始终首先为 Jakarta EE API 类委托 Tomcat 实现的规范(Servlet、JSP、EL、WebSocket)。Tomcat 中的所有其他类加载器都遵循通常的委托模式。

因此,从 Web 应用程序的角度来看,类或资源加载按以下顺序查看以下存储库:

  • JVM 的引导类

  • /WEB-INF/classes 的 Web 应用程序

  • /WEB-INF/lib/*.jar 的 Web 应用程序

  • 系统类加载器类(如上所述)

  • 常见的类加载器类(如上所述)

如果 Web 应用程序类加载器配置了 <Loader delegate=“true”/> 则顺序为:

  • JVM 的引导类

  • 系统类加载器类(如上所述)

  • 常见的类加载器类(如上所述)

  • /WEB-INF/classes 的 Web 应用程序

  • /WEB-INF/lib/*.jar 的 Web 应用程序

XML 解析器和 Java

在旧版本的 Tomcat 中,只需替换 Tomcat libraries 目录中的 XML 解析器,即可更改所有 Web 应用程序使用的解析器。但是,当您运行现代版本的 Java 时,此技术将无效,因为通常的类加载器委托过程将始终优先选择 JDK 内的实现,而不是此实现。

Java 支持一种称为可升级模块的机制,以允许替换在 JCP 之外创建的 API(即 W3C 的 DOM 和 SAX)。它还可用于更新 XML 解析器实现。

请注意,覆盖任何 JRE 组件都会带来风险。如果覆盖组件未提供 100% 兼容的 API(例如,Xerces 提供的 API 与 JRE 提供的 XML API 不是 100% 兼容),则 Tomcat 和/或部署的应用程序可能会遇到错误。

高级配置

还可以配置更复杂的类加载器层次结构。 请参阅下图。默认情况下,未定义 Server 和 Shared 类加载器,并使用上面显示的简化层次结构。 可以通过在 conf/catalina.properties 中定义 server.loader 和/或 shared.loader 属性的值来使用这个更复杂的层次结构。

    启动
      |
    系统
      |
    常见
     /   \
服务器  共享
         /  \
   Webapp1 Webapp2 ...

Server 类加载器仅对 Tomcat 内部可见,对 Web 应用程序完全不可见。

Shared 类加载器对所有 Web 应用程序可见,并可用于跨所有 Web 应用程序共享代码。但是,对此共享代码的任何更新都需要重新启动 Tomcat。