Tomcat为何要打破双亲委派机制
开始之前,我们有个问题可以探讨一下:Tomcat使用默认的双亲委派类加载机制是否可行?
首先,我们可以思考一下,Tomcat作为一个web容器,它需要解决什么问题?
- 假如有若干个应用程序部署在Tomcat上,这些应用程序可能会依赖到同一第三方类库的不同版本,因此Tomcat必须支持每个应用程序的类库可以相互隔离
- 部署在同一个Tomcat上的不同应用程序,相同类库的相同版本应该是共享的,否则就会出现大量相同的类加载到虚拟机中
- Tomcat本身也有依赖的类库,与应用程序依赖的类库可能会混淆,基于安全考虑,应该将两者进行隔离
- 要支持Jsp文件修改后,其生成的class能在不重启的情况下及时被加载进JVM
那么,采用默认的双亲委派类加载机制,能否解决上述问题呢?
- 问题1、3,如果Tomcat采用默认的双亲委派加载机制,是无法加载同一类库不同版本的类的,因为默认的双亲委派加载机制在加载类时,是通过类的全限定名做唯一性校验的
- 问题2,默认的双亲委派类加载机制可以实现,因为它本就能保证唯一性
- 问题4,我们知道Jsp文件更新其实也就是class文件更新了,此时类的全限定名并没有改变,修改Jsp文件后,类加载器会从方法区中直接取到已存在的,这会导致修改后Jsp文件其实不会重新加载。那么,如果直接卸载掉这个Jsp文件的类加载器,再重新创建类加载器去加载修改后的Jsp文件,不就能解决问题了吗?那么你应该能猜到每个Jsp文件应对应一个唯一的类加载器吧
到此,我们可以得出答案,Tomcat只使用默认的双亲委派类加载机制是不可行的!
Tomcat中的自定义类加载器
Tomcat的几个主要的自定义类加载器
- CommonClassLoader:公共的类加载器,其加载的class可以被Tomcat容器本身以及各个Webapp访问
- CatalinaClassLoader:私有的类加载器,其加载的class对于Webapp不可见
- ShareClassLoader:各个Webapp共享的类加载器,其加载的class对于所有Webapp可见,但对于Tomcat容器本身不可见
- WebappClassLoader:各个Webapp私有的类加载器,其加载的class只对当前的Webapp可见
从上面的图,不难看出:
CommonClassLoader
能加载的类都可以被CatalinaClassLoader
和ShareClassLoader
使用,从而实现公有类库的公用,而CatalinaClassLoader
和ShareClassLoader
各自加载的类则与对方相互隔离WebappClassLoader
可以使用ShareClassLoader
加载的类,但各个WebappClassLoader
之间相互隔离JasperLoader
的加载范围仅仅是这个JSP文件所编译出来的那个.class文件,一对一的设计是为了随时丢弃它,当Tomcat检测到JSP文件被修改时,会替换掉当前的JasperLoader
的实例,并通过再一次建立一个新的JasperLoader
实例来实现JSP文件的热加载功能
由此可知,Tomcat的设计是违背Java的双亲委派模型的,每个WebappClassLoader
加载自己目录下的.class文件,不会传递给父加载器,这就打破了双亲委派机制,这样做正是为了实现隔离性。
下面这段代码模拟实现了Tomcat的WebappClassLoader加载自己war包应用内不同版本类实现相互共存与隔离
1 | java复制代码package org.laugen.jvm; |
运行结果如下:
1 | java复制代码自定义类加载器的父加载器:sun.misc.Launcher$AppClassLoader |
由此可知,在同一个JVM中,两个相同全限定名的类对象可以共存,因为他们的类加载器可以不一样,所以看两个类对象是否是同一个时,出了要看类的全限定名外,还需要看他们的类加载器是不是同一个
那么,你知道Tomcat的JasperLoader热加载是怎么实现的吗?
本文转载自: 掘金