Tomcat 7 类加载器是如何实现的
概述
与许多服务器应用程序一样,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应用程序完全不可见。
所述 Common 类加载器是将所有的web应用程序可见,并且可以在所有的 web 应用程序被用来共享代码。但是,对此共享代码的任何更新都需要重新启动Tomcat。