3:类加载机制深度解析(Tomcat打破双亲委派机制)(文末有项
2021-04-07 本文已影响0人
_River_
1:Tomcat的类加载机制
以Tomcat类加载为例,Tomcat 如果使用默认的双亲委派类加载机制行不行?
我们思考一下:Tomcat是个web容器, 那么它要解决什么问题:
1: 部署在同一个web容器中相同的类库相同的版本可以共享。
否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机。
2:一个web容器(Tomcat)可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本
不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
3:web容器(Tomcat)也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,
应该让容器的类库和程序的类库隔离开来。
4:web容器(Tomcat)要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,
但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。
回到问题:
问题 1 :默认的类加载器是能够实现的,因为他的职责就是保证唯一性。
问题 2 和 问题3 :如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,
默认的类加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份。
问题 4:我们要怎么实现jsp文件的热加载,jsp 文件其实也就是class文件,那么如果修改了,但类名还是一样,
类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。
那么怎么办呢?
每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,定时任务检查到了,
就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。
2:Tomcat的类加载机制流程图

tomcat的几个主要类加载器:
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,
比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,
实现相互隔离,比如不同war包应用引入了不同的spring版本,这样实现就能加载各自的spring版本;
关系:
commonLoader (最基本的类加载器 实现了公有类库的共用)包含 catalinaLoader 与 sharedLoader (解决问题1)
catalinaLoader(Tomcat私有加载器 只能加载Tomcat私有的类 )(解决问题3)
与 sharedLoader(共享Webapp加载器) 相互隔离
sharedLoader (共享Webapp加载器) (可以加载多个WebappClassLoader)
与 catalinaLoader(Tomcat私有加载器) 相互隔离
WebappClassLoader(单独应用 只能加载本WebappClassLoader下的类) (解决问题2)(解决问题4)
与 WebappClassLoader(单独应用)相互隔离
3:Tomcat的类加载机制总结
Tomcat 这种类加载机制违背了java 推荐的双亲委派模型
tomcat 为了实现隔离性:
1:每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委:派机制。
2:web容器(Tomcat)自己依赖的类库 与 其他 Wep应用程序的类库 隔离;
4:代码实战
参考自定义实现打破双亲委派机制即可
特别注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一样,
所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。
//利用 获取路径 模拟分别是从两个wep包下加载 同类名的类
//创建类加载器1
MyClassLoader classLoader1 = new MyClassLoader("D:/test1");
Class clazz1 = classLoader1.loadClass("com.example.demo.classLoader.User");
//创建类加载器1
MyClassLoader classLoader2 = new MyClassLoader("D:/test2");
Class clazz2 = classLoader2.loadClass("com.example.demo.classLoader.User");
System.out.println(clazz1.getClassLoader());
System.out.println(clazz2.getClassLoader());
//运行结果:
//分别打印出 两个 新创建的类加载器
//MyClassLoaderTest$MyClassLoader@266474c2
//MyClassLoaderTest$MyClassLoader@66d3c617
项目连接
请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/hesuijin-study-project
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/hesuijin-study-project.git
jvm-module项目模块下 classLoader包