1 特点

双亲委派:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

负责依赖:

同时加载它依赖的类

缓存加载:

加载后放到内存里,再次加载时就会直接获取

2 应用

类层次划分,osgi,热部署,代码加密

3 分类

3.1 启动类加载器 Bootstrap Classloader

负责将存放在 JAVA_HOME/lib 目录的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。

BootstrapClassLoader 对 Java程序是不可见的,所以获取时返回了 null,我们也可以通过某一个类的加载器是否为 null 来作为判断该类是不是使用 BootstrapClassLoader 进行加载的依据

3.2 扩展类加载器 Extension ClassLoader

sun.misc.Launcher$ExtClassLoader ,负责加载 JAVA_HOME\lib\ext,java.ext.dirs 系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

3.3 应用程序类加载器 Application ClassLoader

sun.misc.Launcher$AppClassLoader, 负责加载用户类路径上所指定的类库,可以直接使用这个类加载器。

3.4 系统类加载器system class loader

默认是应用程序类加载器

 系统类加载器配置:

使用如下配置,可以将自定义的类加载器配置成系统类加载器

-Djava.system.class.loader=me.capthua.advancedjava.week1.classloader.MyClassLoader

此时,自定义类必须实现如下的构造方法

public MyClassLoader(ClassLoader classLoader){super(classLoader);
}

3.5 自定义类加载器

示例代码如下:

package me.ffulauh.javalang;import java.io.IOException;
import java.io.InputStream;public class ClassLoaderTest {public static void main(String[] args)throws Exception {ClassLoader myLoader=new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {try {String fileName=name.substring(name.lastIndexOf(".")+1)+".class";InputStream is=getClass().getResourceAsStream(fileName);if(is==null){return super.loadClass(name);}byte[] b=new byte[is.available()];is.read(b);return defineClass(name,b,0,b.length);} catch (IOException e){throw new ClassNotFoundException(name);}}};Object obj=myLoader.loadClass("me.ffulauh.javalang.ClassLoaderTest").newInstance();System.out.println(obj.getClass());//class me.ffulauh.javalang.ClassLoaderTestSystem.out.println(obj.getClass().getClassLoader());//me.ffulauh.javalang.ClassLoaderTest$1@d716361System.out.println(me.ffulauh.javalang.ClassLoaderTest.class.getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2System.out.println(obj instanceof me.ffulauh.javalang.ClassLoaderTest);//false}
}

上面自定义的类加载器的逻辑是:从资源文件中读入一个字节码文件的输入流,如果流为空,则让父 类加载器加载,否则,由自定义的类加载器加载。

此逻辑为 子 类加载器 优先加载。不属于双亲委派机制。当然这里的逻辑有开发者自定定义。也可以显然 父 类加载器加载。

自定义类加载器的 父 类加载器是应用程序类加载器:

4 类加载器源码分析

4.1 ClassLoader

ClassLoader中的主要方法为:loadClass(String name, boolean resolve) 方法中 resolve 表示是否连接,默认为 false

先检查是否已经被加载过,若没有加载则调用父加载器的loadClass(),若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。findClass()需要子类实现。

ClassLoader.java
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}

4.2 URLClassLoader

AppClassLoader,ExtClassLoader都继承URLClassLoader。

URLClassLoader 中的 findClass 调用的是 ClassLoader中的defineClass(),defineClass 中会调用 SecureClassLoader 中的 defineClass方法。

URLClassLoader.java
protected Class<?> findClass(final String name)throws ClassNotFoundException
{......return defineClass(name, res);......
}
private Class<?> defineClass(String name, Resource res) throws IOException {......return defineClass(name, b, 0, b.length, cs);......
}SecureClassLoader.java
protected final Class<?> defineClass(String name,byte[] b, int off, int len,CodeSource cs)
{return defineClass(name, b, off, len, getProtectionDomain(cs));
}

ClassLoader中的的 addURL(URL url) 

这个方法是将特定的 url 添加到类加载器,这些 url 是用来搜索类与资源的。将类注入现有累加载器是会用到这个方法。

/*** Appends the specified URL to the list of URLs to search for classes and resources.* If the URL specified is null or is already in the list of URLs, or if this loader is closed, then invoking this method has no effect.*/
protected void addURL(URL url) {ucp.addURL(url);
}

5.添加引用类的几种方式

1. 将类放到指定目录

放到 JDK 的 JAVA_HOME/lib/ext 下, -Djava.ext.dirs 指定目录, java -cp/classpath 指定类路径

2. 自定义 ClassLoader 加载

3. 将类注入已有的ClassLoader。拿到当前执行类的 ClassLoader,反射调用 defineClass 或者 addUrl方法 让已有的类加载器加载新类。

代码如下:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();String byteFilePath = "E:\\ideaProjects\\tech4u\\src\\main\\java\\me\\ffulauh\\javalang\\jvm\\classloader" +"\\HelloWorld.class";
File byteFile=new File(byteFilePath);
byte[] classBytes= FileUtils.file2byte(byteFile);
String className="me.ffulauh.javalang.jvm.classloader.HelloWorld";
Method defineMethod = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class);
defineMethod.setAccessible(true);
Class helloClazz =(Class) defineMethod.invoke(classLoader,className, classBytes, 0, classBytes.length);
Method method=helloClazz.getMethod("sayHello");
method.invoke(helloClazz.newInstance());String jarFile="D:\\devtools\\repo\\com\\alibaba\\druid\\1.2.6\\druid-1.2.6.jar";
Class jarClazz=null;
Method addUrlMethod= URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
addUrlMethod.invoke(classLoader,new File(jarFile).toURI().toURL());
jarClazz=Class.forName("com.alibaba.druid.pool.DruidPooledStatement");
System.out.println(jarClazz);

6 遇到的问题

1. 如果一个类加载器已经加载了一个类,它再次调用defineClass()方法加载这个类时,会抛异常

Exception in thread "main" java.lang.LinkageError: loader (instance of  me/ffulauh/javalang/opbytecode/asm/simple/MyClassLoader): attempted  duplicate class definition for name: "me/ffulauh/javalang/opbytecode/asm/HelloWorld"at java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:757)at java.lang.ClassLoader.defineClass(ClassLoader.java:636)at me.ffulauh.javalang.opbytecode.asm.simple.MyClassLoader.findClass(MyClassLoader.java:28)at me.ffulauh.javalang.opbytecode.asm.simple.MyClassLoader.loadClass(MyClassLoader.java:20)at me.ffulauh.javalang.opbytecode.asm.transform.ModifyMethodDemo.main(ModifyMethodDemo.java:38)

2. 在Tomcat 8中,处理请求的线程对应的类加载器为 TomcatEmbeddedWebappClassLoader,这个类加载器的 addURL方法是没有实现的。此时如果调用这个方法,是不会加载不到类的。

Java类加载器详解 1相关推荐

  1. Java类加载器详解

    Java虚拟机中的类加载有三大步骤:,链接,初始化.其中加载是指查找字节流(也就是由Java编译器生成的class文件)并据此创建类的过程,这中间我们需要借助类加载器来查找字节流. Java虚拟机默认 ...

  2. Java类加载机制详解【java面试题】

    Java类加载机制详解[java面试题] (1)问题分析: Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数 ...

  3. 异常将上下文初始化事件发送到类的侦听器实例._Java CLassLoader类加载器详解,一点课堂(多岸学院)...

    Java CLassLoader 类加载器(class loader)是 Java™中的一个很重要的概念.类加载器负责加载 Java 类的字节代码到 Java 虚拟机中.本文首先详细介绍了 Java ...

  4. Java class类文件和类加载器详解以及代码优化

    JVM就是Java虚拟机,它是Java程序运行的载体. 计算机只识别0和1.Java是⾼级语⾔.⾼级语⾔编写的程序要想被计算机执⾏,需要变成⼆进制形式的本地机器码.能直接变成机器码的语义是C++,它的 ...

  5. Java高新技术第一篇:类加载器详解

    首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...

  6. ccs加载out文件_类加载流程、类加载机制及自定义类加载器详解

    原文:juejin.im/post/5cffa528e51d4556da53d091 一.引言 当程序使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.链接.初始化三个步骤对该类进行类加载 ...

  7. Java 类加载机制详解

    一.类加载器 类加载器(ClassLoader),顾名思义,即加载类的东西.在我们使用一个类之前,JVM需要先将该类的字节码文件(.class文件)从磁盘.网络或其他来源加载到内存中,并对字节码进行解 ...

  8. 类加载器源码、双亲委派、自定义类加载器详解

    文章目录 jdk的类加载器 双亲委派 自定义类加载器 打破双亲委派 jdk的类加载器 查看一个类的类加载器: ClassLoader classLoader = boy.class.getClassL ...

  9. Java基础学习总结(115)——Java 类加载机制详解

    一.类加载器 类加载器(ClassLoader),顾名思义,即加载类的东西.在我们使用一个类之前,JVM需要先将该类的字节码文件(.class文件)从磁盘.网络或其他来源加载到内存中,并对字节码进行解 ...

最新文章

  1. 【LeetCode从零单排】No15 3Sum
  2. 用JavaScript获取表单里的值
  3. Vue 第九天学习
  4. DCMTK:DSRListOfItems类的测试程序
  5. Linux console on LCD
  6. junit:junit_JUnit和Hamcrest:在assertEquals上进行改进
  7. 10.15 lzxkj
  8. 腾讯云发布“小程序·云开发十大优秀实践”,猫眼、唯品会、香格里拉等入选
  9. yum 安装jenkins
  10. 良心安利三大游戏音效素材网站
  11. 什么是编程?为什么要编程?
  12. 泰坦尼克号幸存者预测(分类)
  13. LLVM IR转CFG
  14. 中国偶氮二异丁腈市场供需动态与投资前景展望报告(新版)2022-2027年
  15. Python将Excel文件插入Mysql数据库(脚本)
  16. mysql 数据库基本知识
  17. (ASCII代码 )密码破译
  18. chatbot使用_如何使用Python构建Chatbot项目
  19. CART树二元分类算法Python实现
  20. ecef转换经纬度坐标 c语言代码,WGS84和ECEF坐标的转换

热门文章

  1. 激光雷达传感器c语言编程,基于SPAD、SiPM和ToF传感器的激光雷达电路设计对比,谁才是激光雷达的未来?...
  2. SAP的Smartform 自建页格式:SPAD
  3. maven项目统一管理版本号方案
  4. Solidworks安装SW2URDF插件安装后无法正常加载问题
  5. 基于个人信用评分的建模分析和授信决策
  6. 上课笔记-台大哲学概论(一)
  7. win10-microsoft store卸载了怎么安装回来
  8. v-rep 室内场景
  9. apriori算法c++实现_经典数据结构与算法(四):Python/C/C ++实现队列类型双端队列数据结构...
  10. 【学习笔记】Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程、手写 Promise(二、JavaScript 异步编程)