先看Servlet规范2.4版中9.7.2章节的一段话
翻译:
一个容器用于加载 WAR 文件中的 Servlet 的类加载器必须允许开发者按照 J2SE 的标准方式使用 getResource
方法加载 WAR 中库 JAR 文件内的任何资源。但它不能允许 WAR 覆盖 J2SE 或 Java Servlet API 的类。建议该加载器不允许 WAR 中的 Servlet 访问 Web 容器的实现类。同时,建议应用程序类加载器实现时优先加载 WAR 内的类和资源,而不是加载容器范围内的库 JAR 文件中的类和资源。
Tomcat为什么打破双亲委派模型
先看总结
Tomcat打破双亲委派模型的主要原因是为了解决Web应用程序的类加载冲突问题,并提供更好的灵活性和可扩展性。双亲委派模型在一般情况下是有益的,可以确保类加载的安全性和一致性,但在Web应用程序的场景下,它可能会导致一些问题:
1. 类加载冲突: Web应用程序通常需要依赖一些共享的类库,这些类库可能由容器提供或者由应用程序自身提供。当使用双亲委派模型时,容器的类加载器会先尝试加载共享类库,导致Web应用程序中提供的同名类无法被正确加载,从而产生类加载冲突。
2. 限制灵活性:双亲委派模型要求类加载器在委派给父加载器之前尝试加载类,这可能会限制Web应用程序自定义类加载的灵活性。有些Web应用程序可能需要加载自定义的类或资源,而不希望受到容器类加载器的限制。
为了解决这些问题,Tomcat采用了自定义的Web应用程序类加载器,它打破了双亲委派模型的一部分规则,具体表现在:
1. Web应用程序类加载器首先会查找Web应用程序内部的类和资源,而不是立即委派给父类加载器。这样可以确保Web应用程序中提供的类能够被正确加载,避免类加载冲突。
2. 对于JRE基本类(如Java标准库类),Web应用程序类加载器不能覆盖。这是为了保证Java核心类的一致性和稳定性。
3. 对于JavaEE API类(如Servlet、JSP、EL、WebSocket等),Web应用程序类加载器会先委派给父类加载器加载,确保使用Tomcat实现的JavaEE规范。
通过打破双亲委派模型的一部分规则,Tomcat提供了更灵活的类加载策略,允许Web应用程序自定义类加载器加载自己的类和资源,避免了类加载冲突问题,同时确保了Java核心类的稳定性和JavaEE规范的正确实现。这样的设计使得Tomcat能够更好地支持各种复杂的Web应用程序,并提供了更高的可扩展性和自定义能力。
详细如下
这是官方的一幅图,接下来介绍里面各个加载器的作用
-
Bootstrap — 该类加载器包含 Java 虚拟机提供的基本运行时类,以及系统扩展目录 (
$JAVA_HOME/jre/lib/ext
) 中存在的 JAR 文件中的任何类。 注意:某些 JVM 可能会将其实现为多个类加载器,或者它可能根本不可见(作为类加载器)。 -
System— 该类加载器通常从环境变量的内容初始化
CLASSPATH
。所有这些类对于 Tomcat 内部类和 Web 应用程序都是可见的。但是,标准 Tomcat 启动脚本($CATALINA_HOME/bin/catalina.sh
或%CATALINA_HOME%\bin\catalina.bat
)完全忽略环境变量本身的内容CLASSPATH
,而是从以下存储库构建系统类加载器:-
$CATALINA_HOME/bin/bootstrap.jar
-
$CATALINA_BASE/bin/tomcat-juli.jar或 $CATALINA_HOME/bin/tomcat-juli.jar
-
$CATALINA_HOME/bin/commons-daemon.jar
-
-
Common — 该类加载器包含其他类,这些类对 Tomcat 内部类和所有 Web 应用程序都可见。
通常,应用程序类不应放置 在这里。该类加载器搜索的位置由 $CATALINA_BASE/conf/catalina.properties 中的属性定义
common.loader
。默认设置将按列出的顺序搜索以下位置:- 解压后的类和资源
$CATALINA_BASE/lib
- JAR 文件位于
$CATALINA_BASE/lib
- 解压后的类和资源
$CATALINA_HOME/lib
- JAR 文件位于
$CATALINA_HOME/lib
- 解压后的类和资源
-
WebappX — 为部署在单个 Tomcat 实例中的每个 Web 应用程序创建一个类加载器。
/WEB-INF/classes
Web 应用程序目录中的所有解压类和资源,以及/WEB-INF/lib
Web 应用程序目录下 JAR 文件中的类和资源,对此 Web 应用程序可见,但对其他应用程序不可见。
翻译:
如上所述,Web 应用程序类加载器与默认的 Java 委托模型不同(根据 Servlet 规范 2.4 版第 9.7.2 Web 应用程序类加载器第 9.7.2 节中的建议)。当处理从 Web 应用程序的WebappX类加载器加载类的请求时,该类加载器将首先在本地存储库中查找,而不是在查看之前进行委托。但也有例外。作为 JRE 基类一部分的类不能被覆盖。有一些例外,例如可以使用适当的 JVM 功能覆盖 XML 解析器组件,该功能是 Java <= 8 的认可标准覆盖功能和 Java 9+ 的可升级模块功能。最后,Web 应用程序类加载器将始终首先委托 Tomcat 实现的规范(Servlet、JSP、EL、WebSocket)的 JavaEE API 类。Tomcat 中的所有其他类加载器都遵循通常的委托模式。
因此,从 Web 应用程序的角度来看,类或资源加载按以下顺序查找以下存储库:
- JVM 的引导类
- /WEB-INF/您的 Web 应用程序的类
- Web 应用程序的/WEB-INF/lib/*.jar
- 系统类加载器类(如上所述)
- 常见的类加载器类(如上所述)
来自: