类加载机制源码解析&双亲委派机制&自定义类加载器&如何打破双亲委派&Tomcat类加载机制(如何实现多服务相互隔离相互共存)&jsp热加载的底层如何实现
2021-11-20 00:25:58 0 举报
类加载机制源码解析&双亲委派机制&自定义类加载器&如何打破双亲委派&Tomcat类加载机制(如何实现多服务相互隔离相互共存)&jsp热加载的底层如何实现
作者其他创作
大纲/内容
2、判断是否已经加载过了
只要上层加载过的类,下层就不会再次加载。
java Xxxx.class==》在Windows上 java.exe 调用jvm.dll文件通过c++创建虚拟机
上层加载失败时,才交给下层加载器来处理
向上委托
Launcher
保护jdk核心包的类不被篡改,因为这些核心包都是由引导类加载器加载核心包路径下的类。
1、加锁通过concurrentHashMap保证线程安全,并通过putIfAbsent放入要加载的类的className来加锁
C++调用java代码创建JVM启动器(sun.mis.Laucher)
AppClassLoader
比如用户自己写的同名String不会被加载,因为委托顶级类加载器时已经被加载了。
正常自定义类加载器
引导类加载器是c++的,因此调用的native方法
自定义类加载器以及如何打破双亲委派机制
WebappClassLoader负责加载war1
不是父子关系,只是上下层关系,共同继承于URLClassLoader
package com.sgcc.controller;import java.io.FileInputStream;/** * 自定义类加载器:加载指定路径下的包 */public class MyClassLoader extends ClassLoader { //自定义类加载器 负责扫描的包绝对路径 private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } /** * 重写加载类的核心逻辑 * @param name class的相对路径 * @return * @throws ClassNotFoundException */ protected Class<?> findClass(String name) throws ClassNotFoundException { try{ //文件相对路径的 . 替换成 / ,用于加载class。 String url=classPath+\"/\"+name.replaceAll(\"\\\\.\
N4、如果没有上层类加载器,就委托最顶层:引导类加载器加载
super
负责加载支撑JVM运行的,位于JRE的lib下的ext扩展类库:JAR包
Class<?> c = findLoadedClass(name);
如果没有加载过,判断该类加载器是否有上层类加载器
加载方法
ExtClassLoader扩展类加载器
IcActivityAccount2区分的特定方法
JasperLoader负责加载jsp1
CommonClassLoader负责加载common文件夹下lib(Tomcat最基本的类)
Y3、委托上层类加载器进行加载
5、使用当前的类加载器加载
C++获取加载器调用的方法
c = findClass(name);
java命令启动main方法底层流程:
系统类加载器
负责加载用户编写的classpath路径下的代码
文本
每层类加载器都有自己特定的加载路径。因此只会到对应的路径下,找对应的类进行加载。如果指定 的类不在他的路径下,将无法加载。比如扩展类加载器就是加载java.ext.dirs包下的。系统类加载器就是加载java.class.path下的。
总结:java.exe调用java.dll创建JVM虚拟机,然后创建一个c++的引导类加载器实例。引导类实例 调用java代码创建 JVM启动器Launcher,Launcher调用getClassLoader获取要调用的类的对应类加载器,类加载类加载目标类
类加载机制源码解析&双亲委派机制&自定义类加载器&如何打破双亲委派&Tomcat类加载机制(如何实现多服务相互隔离相互共存)&jsp热加载的底层如何实现
C++
Y
Tomcat的jsp热加载功能
Tomcat实现多war包相互隔离加载就是通过这种方式打破双亲委派机制。
private native final Class<?> findLoadedClass0(String name);
ClassLoader
打破双亲委机制的自定义类加载器
启动类加载器(引导类加载器)
扩展类加载器
Tomcat类加载器
CatalinaClassLoader负责加载server文件夹下lib(tomcat容器的私有类,对wabapp不可见)
沙箱保护机制
加载范围仅仅是这个jsp文件所编译出来的那一个.class文件,文件变化就丢弃这个加载器
c = findBootstrapClassOrNull(name);
public Launcher() { Launcher.ExtClassLoader var1;//定义ExtClassLoader var1 = Launcher.ExtClassLoader.getExtClassLoader(); //获取系统类加载器,赋值给启动器的属性变量loaderthis.loader = Launcher.AppClassLoader.getAppClassLoader(var1);Thread.currentThread().setContextClassLoader(this.loader);...... }
.getAppClassLoader(var1)传的是扩展类加载器
c==null上层类加载器和引导类加载器都没有加载成
class com.sgcc.controller.MyClassLoader2这是spring4class com.sgcc.controller.MyClassLoader2这是spring5
自定义类加载器2
当web检测到jsp修改时,就会替换调JasperLoader的实例,并通过再建立一个新的jsp类加载器来实现jsp文件的 热加载功能
所有的类加载器都是继承于ClassLoader,类加载器加载类时,最终都会走这个方法。
为什么要设计双亲委派机制?
公共依赖包加载器
直接返回
SharedClassLoader负责加载share文件夹下lib(各webapp共享的类)
package com.sgcc.controller;import java.io.FileInputStream;/** * 自定义类加载器:加载指定路径下的包 * 打破双亲委派机制 版本 */public class MyClassLoader2 extends ClassLoader { //自定义类加载器 负责扫描的包绝对路径 private String classPath; public MyClassLoader2(String classPath) { this.classPath = classPath; } /** * 重写加载类的核心逻辑 * @param name class的相对路径 * @return * @throws ClassNotFoundException */ protected Class<?> findClass(String name) throws ClassNotFoundException { try{ //文件相对路径的 . 替换成 / ,用于加载class。 String url=classPath+\"/\"+name.replaceAll(\"\\\\.\
负责加载支撑JVM运行的,位于JRE的lib核心类库。这个类加载器是C++的,比如你打印一下String的类加载器就是null
public void sout(){ System.out.println(\"这是spring5\"); }
为什么所有的类都是先从系统类加载器往上委托的?
打印的结果
这里初始化loader属性为系统类加载器。并指定系统类加载器的上层加载器为扩展类加载器。
双亲委派机制源码剖析
public ClassLoader getClassLoader() { return this.loader; }
自定义类加载器1
Java
Tomcat类加载机制(如何实现多服务相互隔离相互共存)&jsp热加载的底层如何实现
private ClassLoader loader;
public class T { public static void main(String[] args) throws ClassNotFoundException { MyClassLoader myClassLoader =new MyClassLoader(\"D:/A\"); Class<?> icActivityAccount = myClassLoader.loadClass(\"com.sgcc.model.IcActivityAccount\"); System.out.println(icActivityAccount.getClassLoader().getClass());//class sun.misc.Launcher$AppClassLoader // 当项目对应路径下也有这个文件,也就是系统类加载器对应的路径下游这个类,因为上层优先加载,所以这里的类加载器还是AppClassLoader Class<?> icActivityAccount2 = myClassLoader.loadClass(\"com.sgcc.model.IcActivityAccount2\"); System.out.println(icActivityAccount2.getClassLoader().getClass());//class com.sgcc.controller.MyClassLoader //项目没有这个类,就会正常按自定义类加载器加载 MyClassLoader2 myClassLoader2=new MyClassLoader2(\"D:/A2\"); Class<?> aClass = myClassLoader2.loadClass(\"com.sgcc.model.IcActivityAccount\"); //这里虽然项目中有这个类,但是我们在findClass方法中,修改了逻辑,只要是com.sgcc.model开头的类,关闭双亲委派机制,只由自定义类加载器加载 System.out.println(aClass.getClassLoader().getClass());//class com.sgcc.controller.MyClassLoader2 }}
各webapp私有的类
JasperLoader负责加载jsp2
避免类的重复加载
//自定义一个打破双亲委派机制的类加载器。部署一个war包就创建一个类加载对象(只是路径不同)。这样就可以将一个服务是spring4版本的代码加载,一个spring5版本的代码加载,相互隔离。public class T { public static void main(String[] args) throws Exception { MyClassLoader2 myClassLoader2=new MyClassLoader2(\"D:/A2\"); Class<?> aClass = myClassLoader2.loadClass(\"com.sgcc.model.IcActivityAccount2\"); //打印类加载器 System.out.println(aClass.getClassLoader().getClass()); //获取方法体,区分两个版本的IcActivityAccount2 Method sout = aClass.getDeclaredMethod(\"sout\
WebappClassLoader负责加载war2
由此可知,C++调用启动器获取类加载器时,就是先获取的系统类加载器
0 条评论
回复 删除
下一页