Java面试题18-tomcat为什么要使用自定义的类加载器
一、tomcat类加载器的设计
Tomcat使用默认的类加载机制行不行 ?
跟着我的思路思考一下:Tomcat是个web容器,web容器要解决什么问题?
- 一个web容器可能要部署多个应用程序,不同的应用程序之间可能会依赖同一个第三方库的不同版本,不能要求同一个类库在同一个服务器只有一份,所以tomcat要具有隔离性,每个服务都是独立的。
- 部署在同一个web容器的多个应用程序的同一个第三方库的版本可能相同,但是不能要求同一版本的类库在每个服务器都有一份,所以tomcat要具有共享性。
- web容器也有自己依赖的类库,不能与应用程序的类库混淆,出于安全性能考虑,应该让容器的类库和程序的类库隔离开来。
- web容器要支持jsp的修改,因为jsp文件也是要编译成class文件才能在虚拟机中运行,我们要实现热部署的设计,所以web容器应该支持修改jsp之后不用重启。
二、tomcat使用默认的类加载器行不行
答案肯定是不行的。
为什么呢?因为通过我们上面的web容器需要具备的几个特性就知道,如果使用默认的类加载器,是无法实现加载同一类库的不同版本依赖的,默认的类加载器的加载是依据该类的全限定类名的,并且只会加载一个类。
第二个问题,默认的类加载器是能够实现的,因为他的职责就是保证唯一性。第三个问题和第一个问题一样。我们再看第四个问题,我们想我们要怎么实现jsp文件的热修改,jsp 文件其实也就是class文件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。
那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。
三 、Tomcat 如何实现自己独特的类加载机制?
我们看到,前面3个类加载和默认的一致,CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载*/common/、/server/、/shared/*(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。
- commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
- catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
- sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
- WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
从图中的委派关系中可以看出:
CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。
WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。
四、tomcat为什么要自定义三个类加载器
- commonLoader
- catalinaLoader
- sharedLoader
(1)实现相同jar包不同版本之前的隔离。
(2)多个服务要共享相同jar包相同版本。
(3)避免类加载器的内存泄漏。
(4)服务器自身jar包的依赖与应用程序之间依赖的隔离。
(5)热部署的设计与实现。