JAVA虚拟机中是如何加载JAVA类的?( 四 )


编写自定义类加载器时 。如果没有太过于复杂的需求 。可以直接继承URLClassLoader类 。这样就可以避免自己去编写findClass()方法及其获取字节码流的方式 。使自定义类加载器编写更加简洁
ClassLoader常用方法
ClassLoader类 。是一个抽象类 。其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)
方法描述getParent()返回该类加载器的超类加载器loadClass(String name)加载名称为name的类 。返回java.lang.Class类的实例findClass(String name)查找名称为name的类 。返回java.lang.Class类的实例findLoadedClass(String name)查找名称为name的已经被加载过的类 。返回java.lang.Class类的实例defineClass(String name, byte[] b, int off, int len)把字节数组b中内容转换为一个Java类 。返回java.lang.Class类的实例resolveClass(Class<?> c)连接指定的一个Java类对类加载器的引用
JVM必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的 。如果一个类型是由用户类加载器加载的 。那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中 。当解析一个类型到另一个类型的引用的时候 。JVM需要保证这两个类型的类加载器是相同的 。
双亲委派机制
Java虚拟机对class文件采用的是按需加载的方式 。也就是说当需要使用该类的时候才会将它的class文件加载到内存生成class对象 。而且加载某个类的class文件时 。Java虚拟机采用的是双亲委派模式 。即把请求交给父类处理 。它是一种任务委派模式 。
工作过程
如果一个类加载器收到了类加载请求 。它并不会自己先去加载 。而是把这个请求委托给父类的加载器去执行;
如果父类加载器还存在其父类加载器 。则进一步向上委托 。依次递归 。请求最终将到达顶层的启动类加载器;
如果父类加载器可以完成类加载任务 。就成功返回 。倘若父类加载器无法完成此加载任务 。子加载器才会尝试自己去加载 。这就是双亲委派模式

JAVA虚拟机中是如何加载JAVA类的?

文章插图
优势
避免类的重复加载 。JVM中区分不同类 。不仅仅是根据类名 。相同的class文件被不同的ClassLoader加载就属于两个不同的类(比如 。Java中的Object类 。无论哪一个类加载器要加载这个类 。最终都是委派给处于模型最顶端的启动类加载器进行加载 。如果不采用双亲委派模型 。由各个类加载器自己去加载的话 。系统中会存在多种不同的Object类)
保护程序安全 。防止核心API被随意篡改 。避免用户自己编写的类动态替换 Java的一些核心类 。比如我们自定义类:java.lang.String
在JVM中表示两个class对象是否为同一个类存在两个必要条件:
类的完成类名必须一致 。包括包名
加载这个类的ClassLoader(指ClassLoader实例对象)必须相同
沙箱安全机制
如果我们自定义String类 。但是在加载自定义String类的时候会率先使用引导类加载器加载 。而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java\lang\String.class) 。报错信息说没有main方法就是因为加载的是包中的String类 。这样就可以保证对java核心源代码的保护 。这就是简单的沙箱安全机制 。
破坏双亲委派模型
双亲委派模型并不是一个强制性的约束模型 。而是Java设计者推荐给开发者的类加载器实现方式 。可以“被破坏” 。只要我们自定义类加载器 。重写loadClass()方法 。指定新的加载逻辑就破坏了 。重写findClass()方法不会破坏双亲委派 。
双亲委派模型有一个问题:顶层ClassLoader 。无法加载底层ClassLoader的类 。典型例子JNDI、JDBC 。所以加入了线程上下文类加载器(Thread Context ClassLoader),可以通过设置该类加载器 。然后顶层ClassLoader再使用获得底层的ClassLoader进行加载 。
Tomcat中使用了自定ClassLoader 。并且也破坏了双亲委托机制 。每个应用使用WebAppClassloader进行单独加载 。他首先使用WebAppClassloader进行类加载 。如果加载不了再委托父加载器去加载 。这样可以保证每个应用中的类不冲突 。每个tomcat中可以部署多个项目 。每个项目中存在很多相同的class文件(很多相同的jar包) 。他们加载到jvm中可以做到互不干扰 。
【JAVA虚拟机中是如何加载JAVA类的?】利用破坏双亲委派来实现代码热替换(每次修改类文件 。不需要重启服务) 。因为一个Class只能被一个ClassLoader加载一次 。否则会报 。当我们想要实现代码热部署时 。可以每次都new一个自定义的ClassLoader来加载新的Class文件 。JSP的实现动态修改就是使用此特性实现 。