1.简介
ClassLoader,顾名思义就是类加载器,负责将Class加载到JVM中,将Class字节码重新解析成JVM统一要求的对象格式,除此之外它还负责审查每个类应该由谁加载,它是一种父优先的等级加载机制。
2.ClassLoader类结构分析
1)protect ... defineClass(...)
将一个字节数组转换为 Class 类的实例。
注:如果直接调用这个方法生成类的Class对象,这个类的Class对象还没有resolve,这个resolve将会在这个对象真正实例化时才进行。
2)protect ... findClass(...)
使用指定的二进制名称查找类。默认实现抛出一个ClassNotFoundException,我们通过覆盖它来实现类的加载规则,从而取得要加载类的字节码,然后调用defineClass方法生成类的Class对象,如果你想在类被加载到JVM中时就被连接,那么可以接着调用resolveClass方法。ClassLoader按照委托模型来加载类,在通过父类加载器检查所请求的类失败后,此方法才会被loadClass方法所调用。
3)loadClass
使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:
(1)调用 findLoadedClass(String) 来检查是否已经加载类。
(2)在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
(3)调用 findClass(String) 方法查找类。
(4)如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。
鼓励用 ClassLoader 的子类重写 findClass(...),而不是此方法。
4)resolveClass
链接指定的类。
3.ClassLoader的等级加载机制
就是指ClassLoader.loadClass方法的步骤,上文已经描述过,源代码如下:
public Class<?> loadClass(String paramString) throws ClassNotFoundException { return loadClass(paramString, false); } protected synchronized Class<?> loadClass(String paramString, boolean paramBoolean) throws ClassNotFoundException { Class localClass = findLoadedClass(paramString); if (localClass == null) { try { if (this.parent != null) { localClass = this.parent.loadClass(paramString, false); } else { localClass = findBootstrapClass0(paramString); } } catch (ClassNotFoundException localClassNotFoundException) { localClass = findClass(paramString); } } if (paramBoolean) { resolveClass(localClass); } return localClass; }
好处:
1)防止重复加载。
2)考虑到安全因素,保证所有的类都由正确的ClassLoader加载。
4.ClassLoader的层级结构
1)Bootstrap ClassLoader
主要加载JVM自身工作需要的类,完全由JVM控制,需要加载哪个类,如何加载都是由JVM自己控制,别人也访问不到这个类,所以这个ClassLoader不遵守前面介绍的加载规则,它仅仅是一个加载工具而已,既没有更高一级的父加载器,也没有子加载器。
2)ExtClassLoader
服务的特定目标是在System.getProperty("java.ext.dirs")目录下,由Bootstrap ClassLoader加载。
3)AppClassLoader
parent是ExtClassLoader,服务的特定目标在System.getProperty("java.class.path")目录下,也就是我们常用的classpath,由Bootstrap ClassLoader加载。
4)自定义ClassLoader
假如不指定,则默认的parent为AppClassLoader,因为默认的parent = getSystemClassLoader(),而该方法返回的正是AppClassLoader。
protected ClassLoader() { SecurityManager localSecurityManager = System.getSecurityManager(); if (localSecurityManager != null) { localSecurityManager.checkCreateClassLoader(); } this.parent = getSystemClassLoader(); this.initialized = true; }
测试:ClassLoaderTest.java
package com.siyuan.test.lang; import java.net.URL; import java.net.URLClassLoader; import sun.util.resources.CalendarData_zh; public class ClassLoaderTest { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { //ExtClassLoader的服务路径 System.out.println(System.getProperty("java.ext.dirs")); //AppClassLoader的服务路径 System.out.println(System.getProperty("java.class.path")); //String由Bootstrap ClassLoader加载 System.out.println("ClassLoader of String : " + String.class.getClassLoader()); //CalendarData_zh由ExtClassLoader加载 System.out.println(CalendarData_zh.class.getClassLoader()); //ClassLoaderTest由AppClassLoader加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println("ClassLoader of ClassLoaderTest : " + classLoader); //StringTest由URLClassLoader加载 ClassLoader diyClassLoader = new URLClassLoader(new URL[]{new URL("file://D:/testworkspace/jdktest/testbin/")}); System.out.println("ClassLoader of StringTest : " + diyClassLoader.loadClass("com.siyuan.test.filter.StringTest").getClassLoader()); //ClassLoader层级结构 classLoader = diyClassLoader; do { System.out.println("ClassLoader info --------------"); System.out.println("name : " + classLoader); System.out.println("parent : " + classLoader.getParent()); System.out.println("class path : " + classLoader.getResource("")); System.out.println("ClassLoader of it : " + classLoader.getClass().getClassLoader()); classLoader = classLoader.getParent(); } while(classLoader != null); } }
运行结果:
C:\Program Files\Java\jdk1.6.0_10\jre\lib\ext;C:\Windows\Sun\Java\lib\ext D:\testworkspace\jdktest\bin ClassLoader of String : null sun.misc.Launcher$ExtClassLoader@addbf1 ClassLoader of ClassLoaderTest : sun.misc.Launcher$AppClassLoader@19821f ClassLoader of StringTest : java.net.URLClassLoader@61de33 ClassLoader info -------------- name : java.net.URLClassLoader@61de33 parent : sun.misc.Launcher$AppClassLoader@19821f class path : file:/D:/testworkspace/jdktest/bin/ ClassLoader of it : null ClassLoader info -------------- name : sun.misc.Launcher$AppClassLoader@19821f parent : sun.misc.Launcher$ExtClassLoader@addbf1 class path : file:/D:/testworkspace/jdktest/bin/ ClassLoader of it : null ClassLoader info -------------- name : sun.misc.Launcher$ExtClassLoader@addbf1 parent : null class path : null ClassLoader of it : null
5.加载方式
1)隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类。例如,当我们在类中继承或者引用某个类时,JVM在解析当前这个类时发现引用的类不在内存中,那么就会自动将这些类加载到内存中。
2)显式加载:在代码中通过调用ClassLoader来加载类,如this.getClassLoader().loadClass(xxx)或者Class.forName(xxx)。
6.class文件加载过程
1)找到.class文件并把这个文件包含的字节码加载到内存中
2.1)字节码验证,确保格式正确,行为正确
2.2)类准备,包含每个类中定义的字段,方法和实现接口的结构分析和内存分配。
2.3)解析,装入类所引用的其他所有类,如超类,接口,字段,方法签名,方法中使用的变量。
3)类中静态属性和初始化赋值,以及静态块的执行
7.如何判断两个class是否相同
1)它们的完整类名是否一致
2)加载它们的ClassLoader是否是同一个
测试:ClassLoaderTest.java
package com.siyuan.test.lang; import java.net.URL; import java.net.URLClassLoader; import sun.util.resources.CalendarData_zh; public class ClassLoaderTest { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { ClassLoader diyClassLoader1 = new URLClassLoader(new URL[]{new URL("file://D:/testworkspace/jdktest/testbin/")}); Class stringTestClass1 = diyClassLoader1.loadClass("com.siyuan.test.filter.StringTest"); ClassLoader diyClassLoader2 = new URLClassLoader(new URL[]{new URL("file://D:/testworkspace/jdktest/testbin/")}); Class stringTestClass2 = diyClassLoader2.loadClass("com.siyuan.test.filter.StringTest"); System.out.println(stringTestClass1 == stringTestClass2); } }
运行结果
false
8.参考资料
《深入分析 Java Web 技术内幕》
相关推荐
NULL 博文链接:https://lz12366.iteye.com/blog/735289
Sun 官方关于 ClassLoader原理的文章,值得一看
Java 虚拟机中ClassLoader 相关简介 双亲委托机制 Android 中ClassLoader 简介
ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的
自定义classloader的使用
破解java加密的ClassLoader.java,在classloader植入破解代码
ClassLoader原理,ClassLoader原理 ClassLoader原理
Java ClassLoader定制实例
java classloader classpath 张孝祥
关于J2EE服务器的ClassLoader的原理,该文档清晰了揭示了jvm装载类的顺序,同时用户可以自定义修改classLoader的配置 通过该文档,可以加深对Java虚拟机的理解
理解Java ClassLoader机制
用于验证理解Android中Classloader加载类机制的程序demo,从中可以对比DexClassLoader和PathClassLoader的区别联系。
Classloader
ClassLoader类加载机制和原理详解
classloader 源码,自定义classloader
自定义ClassLoader,控制台输入调试。 运行期间 重新载入指定目录的class文件。可实现对于类的功能函数更新。 用到java 反射,@interface 等技术
Java中ClassLoader的解析,从ClassLoader的角度分析了JVM,装载类,创建类的对象的整个过程,更清晰的了解JVM的运行机制。
JVM内存模型,类加载模式工作机制详细,内存屏障,类从被加载到虚拟机内存中开始,直到卸载出内存为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三...