Tomcat 7 类加载器是如何实现的
XML 分析器和 Java
在安全管理器下运行
高级配置
概观
概述
与许多服务器应用程序一样,Tomcat 安装了各种类加载器(即实现的类 java.lang.ClassLoader),以允许容器的不同部分和容器上运行的 Web 应用程序访问可用类和资源的不同存储库。此机制用于提供 Servlet 规范 2.4 版中定义的功能 – 特别是 9.4 和 9.6 节。
在 Java 环境中,类加载器排列在父子树中。通常,当要求类加载器加载特定的类或资源时,它首先将请求委托给父类加载器,然后仅在父类加载器找不到所请求的类或资源时查找它自己的存储库。。请注意,Web 应用程序类加载器的模型与此略有不同,如下所述,但主要原则是相同的。
当 Tomcat 启动时,它会创建一组类加载器,这些加载器被组织成以下父子关系,其中父类加载器位于子类加载器之上:
Bootstrap
|
System
|
Common
/ \
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%bincatalina.bat)完全忽略 CLASSPATH 环境变量本身的内容,而是从以下存储库构建 System 类加载器:
- $CATALINA_HOME/bin/bootstrap.jar – 包含用于初始化 Tomcat 服务器的
main()
方法,以及它依赖的类加载器实现类。 - $CATALINA_BASE/bin/tomcat-juli.jar 或 $CATALINA_HOME/bin/tomcat-juli.jar – 记录实现类。其中包括 java.util.loggingAPI 的增强类,称为 Tomcat JULI,以及 Tomcat 内部使用的 Apache Commons Logging 库的软件包重命名副本。有关详细信息,请参阅日志记录
如果 tomcat-juli.jar 是出现在 $CATALINA_BASE/bin 中,它被用来代替 $CATALINA_HOME/bin 中的那一个。它在某些日志记录配置中很有用
-
$CATALINA_HOME/bin/commons-daemon.jar – 来自 Apache Commons Daemon 项目的类。这个 JAR 文件不存在于CLASSPATH 由脚本 catalina.bat|.sh 构建,但是从 bootstrap.jar 的清单文件中引用。
- Common – 此类加载器包含对 Tomcat 内部类和所有 Web 应用程序都可见的其他类。
通常情况下,应用类应该不 放在这里。此类由 common.loader 加载器搜索的位置 $CATALINA_BASE/conf/catalina.properties 中的属性定义。默认设置将按列出的顺序搜索以下位置:
- 解压缩的类和资源 $CATALINA_BASE/lib
- JAR 文件 $CATALINA_BASE/lib
- 解压缩的类和资源 $CATALINA_HOME/lib
- JAR 文件 $CATALINA_HOME/lib
默认情况下,这包括以下内容:
annotations-api.jar – JavaEE 注释类。
catalina.jar – Tomcat 的 Catalina servlet 容器部分的实现。
catalina-ant.jar – Tomcat Catalina Ant 任务。
catalina-ha.jar – 高可用性包。
catalina-tribes.jar – 群组通信包。
ecj – * .jar – Eclipse JDT Java 编译器。
el-api.jar – EL 2.2 API。
jasper.jar – Tomcat Jasper JSP 编译器和运行时。
jasper-el.jar – Tomcat Jasper EL 实现。
jsp-api.jar – JSP 2.2 API。
servlet-api.jar – Servlet 3.0 API。
tomcat-api.jar – Tomcat 定义的几个接口。
tomcat-coyote.jar – Tomcat 连接器和实用程序类。
tomcat-dbcp.jar – 基于包重命名的 Apache Commons Pool 和 Apache Commons DBCP 1.x 的数据库连接池实现。
tomcat-i18n – **。jar – 包含其他语言资源包的可选 JAR。由于默认捆绑包也包含在每个单独的 JAR 中,因此如果不需要消息的国际化,则可以安全地删除它们。
tomcat-jdbc.jar – 另一种数据库连接池实现,称为 Tomcat JDBC 池。有关详细信息,请参阅 文档
tomcat-util.jar – Apache Tomcat 的各种组件使用的公共类。
tomcat7-websocket.jar – WebSocket 1.1 实现
websocket-api.jar – WebSocket 1.1 API
WebappX – 为部署在单个 Tomcat 实例中的每个 Web 应用程序创建一个类加载器。/WEB-INF/classesWeb 应用程序目录中的所有解压缩的类和资源,以及 Web 应用程序 /WEB-INF/lib 目录下的 JAR 文件中的类和资源,都对此 Web 应用程序可见,但对其他应用程序不可见。
如上所述,Web 应用程序类加载器与默认 Java 委托模型不同(根据 Servlet 规范 2.4 版,第 9.7.2 节 Web 应用程序类加载器中的建议)。当加载从 Web 应用程序的一个类的请求 WebappX 被处理的类加载器,这个类加载器会在本地资源库第一而不是在寻找之前委托。也有例外。作为 JRE 基类的一部分的类不能被覆盖。对于某些类(例如 J2SE 1.4+ 中的 XML 解析器组件),Java 认可的功能可以用于 Java 8. 最后,类加载器将明确忽略包含 Servlet API 类的任何 JAR 文件 – 不包括此类 Web 应用程序中的 JAR。Tomcat 中的所有其他类加载器都遵循通常的委托模式。
因此,从 Web 应用程序的角度来看,类或资源加载按以下顺序查找以下存储库:
- JVM 的 Bootstrap 类
- /WEB-INF/classes 您的 Web 应用程序的类
- /WEB-INF/lib/*.jar 您的 Web 应用程序 Jar 包
- System 类加载器类(如上所述)
- Common 类加载器类(如上所述)
如果配置了 Web 应用程序类加载器,<Loader delegate="true"/>
则清单顺序变为:
- JVM 的 Bootstrap 类
- System 类加载器类(如上所述)
- Common 的类加载器类(如上所述)
- /WEB-INF/classes 您的 Web 应用程序的类
- /WEB-INF/lib/*.jar 您的 Web 应用程序的 Jar 包
XML 分析器和 Java
从 Java 1.4 开始,JRE 中包含一个 JAXP API 和一个 XML 解析器。这会对希望使用自己的 XML 解析器的应用程序产生影响。
在旧版本的 Tomcat 中,您只需替换 Tomcat 库目录中的 XML 解析器即可更改所有 Web 应用程序使用的解析器。但是,当您运行现代版本的 Java 时,此技术将无效,因为通常的类加载器委派过程将始终在 JDK 中选择优先于此实现的实现。
Java 支持一种称为“支持标准覆盖机制”的机制,以允许替换在 JCP 外部创建的 API(即来自 W3C 的 DOM 和 SAX)。它还可用于更新 XML 解析器实现。有关更多信息,请参阅:http://docs.oracle.com/javase/1.5.0/docs/guide/standards/index.html。
Tomcat 通过 -Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS 在启动容器的命令行中包含系统属性设置来利用此机制。此选项的默认值为 $CATALINA_HOME/endorsed。默认情况下不会创建此已签名的目录。请注意,Java 9 不再支持已认可的功能,并且只有在目录 $CATALINA_HOME/endorsed 存在或 JAVA_ENDORSED_DIRS 已设置变量 时才会设置上述系统属性。
请注意,覆盖任何 JRE 组件都存在风险。如果覆盖组件未提供 100%兼容的 API(例如,Xerces 提供的 API 与 JRE 提供的 XML API 不完全兼容),则存在 Tomcat 和 / 或部署的应用程序将出现错误的风险。
在安全管理器下运行
在安全管理器下运行时,允许加载类的位置也取决于策略文件的内容。有关详细信息,请参阅安全管理器 HOW-TO。
高级配置
还可以配置更复杂的类加载器层次结构。见下图。默认情况下,未定义 Server 和 Shared 类加载器,并使用上面显示的简化层次结构。可以通过定义 server.loader 和 / 或 shared.loader 属性的 值来使用这种更复杂的层次结构 conf/catalina.properties。
Bootstrap
|
System
|
Common
/ \
Server Shared
/ \
Webapp1 Webapp2 ...
该服务器类加载器是唯一到 Tomcat 内部可见,并且是 Web 应用程序完全不可见。
所述共享类加载器是将所有的 web 应用程序可见,并且可以在所有的 web 应用程序被用来共享代码。但是,对此共享代码的任何更新都需要重新启动 Tomcat。