Java面试必考问题:Tomcat的类加载模式

前文介绍了双亲委派模型,以及我们在何时需要打破双亲委派模型。本文我们聊一下Tomcat作为一个Web容器,是如何加载类的,是否可以用默认的类加载器机制。

Web容器的类加载需求

一个Web容器可能部署不止一个应用程序,不同的应用程序可能会依赖同一个类库的不同版本,在同一个Tomcat服务器上可能有多份相同类库。因此各个应用程序的类库需要保证相互隔离。另外Web容器与应用程序也可能用了不同版本的类库,为避混淆,容器的类库和程序的类库也需要隔离开。

如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认的类加载器是不管什么版本的,只要全限定类名一样,就只加载一份。

Tomcat为了实现Web容器下各个应用之间的类库隔离,也打破了双亲委派模型的约定。

Tomcat的类加载器

当Tomcat启动时,会创建多种类加载器。除了JVM自带的启动类加载器、扩展类加载器、应用程序类加载器以外,Tomcat自定义的类加载器包括:Common类加载器Catalina类加载器Shared类加载器WebApp类加载器、JSP类加载器等。

其中Common类加载器、Catalina类加载器、Shared类加载器原先分别负责加载Tomcat目录下 /common/*/server/* /shared/* 下的类库,不过Tomcat6这三个目录都已经合并到 CATALINA_HOME/lib 目录中。WebApp类加载器负责加载 /WebApp/WEB-INF/* 中的Java类库。

Tomcat自定义类加载器的区别:

Common类加载器:加载路径的class可以被Tomcat容器本身以及各个Webapp访问;

Catalina类加载器:加载路径的class对于Tomcat容器可见,对于Webapp不可见;

Shared类加载器:加载路径的class对于所有Webapp可见,但对于Tomcat容器不可见;

Webapp类加载器:加载路径中的class只对当前Webapp可见。

Tomcat类加载器

类加载器的工作机制

类加载器的关系图

启动类加载器 扩展类加载器 负责加载JVM启动所需的核心类(jre/lib/*.jar)以及标准扩展类(jre/lib/ext/*.jar)。

应用程序类加载器

负责加载Tomcat启动所需的类,如bootstrap.jar,位于CATALINA_HOME/bin下。通常在 catalina.bat 或者 catalina.sh 中指定。

Common 通用类加载器

加载Tomcat使用的类以及Web应用的通用类,位于 CATALINA_HOME/lib 下,比如servlet-api.jar等。Common类加载器、Catalina类加载器、Shared类加载器在默认的配置中,它们其实都是同一个对象,即commonLoader

WebApp 类加载器

每个应用在部署后,都会创建一个唯一的WebApp类加载器。该类加载器会加载位于 WEB-INF/lib下的jar文件中的类 和 WEB-INF/classes下的class文件。 

当应用需要到某个类时,则会按照以下顺序进行类加载:

  1. 使用启动类加载器加载
  2. 使用应用程序类加载器加载
  3. 使用WebApp类加载器在WEB-INF/classes中加载
  4. 使用WebApp类加载器在WEB-INF/lib中加载
  5. 使用Common类加载器在CATALINA_HOME/lib中加载

Tomcat 为了实现不同应用之间的隔离性,没有遵守双亲委派的约定,每个WebAppClassLoader加载自己目录下的class文件,不会传递给父类加载器。

JSP页面的类加载

每一个JSP文件最终都要编译成class文件才能在虚拟机中运行,每一个JSP页面也对应一个JSP类加载器。

Web容器需要支持JSP文件修改后不用重启。JSP文件如果修改了,但类名还是一样的,类加载器会直接取方法区中已经存在的,如何加载修改后的JSP文件呢?

因为每个JSP文件对应一个唯一的类加载器,当这个JSP文件修改了,那么就直接卸载掉这个jsp页面对应的类加载器,重新创建一个新的类加载器,重新加载JSP文件。 

我会持续更新关于物联网、云原生以及数字科技方面的文章,用简单的语言描述复杂的技术,也会偶尔发表一下对IT产业的看法,欢迎大家关注,谢谢。

举报
评论 0