文章目录

  • Java 自定义类加载器教程
  • loadClass
  • findClass
  • defineClass
  • 函数调用过程

Java 自定义类加载器教程

除了在面试中遇到类的加载器的概率会高外,在实际的工作中很少接触。但是一个程序员想要成长为大牛就必须对一些 JVM 的底层设计有些了解。在此基础上我们阅读一些源码和框架会显得更轻松。

好了废话不多说,我们接着前面的文章,乘热打铁。来实现一个 Java 自定义类加载器吧。

要实现 Java 自定义的类加载器,我们需要继承 ClassLoader 。并且需要了解Java的双亲委派模型。

loadClass

loadClass默认实现如下:

public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);
}

再看看loadClass(String name, boolean resolve)函数:

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}// 业余草:www.xttblog.comif (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;}
}

从上面代码可以明显看出,loadClass(String, boolean)函数即实现了双亲委派模型!整个大致过程如下:

  1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
  2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
  3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
    话句话说,如果自定义类加载器,就必须重写findClass方法!

findClass

findClass的默认实现如下:

protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}

可以看出,抽象类ClassLoader的findClass函数默认是抛出异常的。而前面我们知道,loadClass在父加载器无法加载类的时候,就会调用我们自定义的类加载器中的findeClass函数,因此我们必须要在loadClass这个函数里面实现将一个指定类名称转换为Class对象.

如果是是读取一个指定的名称的类为字节数组的话,这很好办。但是如何将字节数组转为Class对象呢?很简单,Java提供了defineClass方法,通过这个方法,就可以把一个字节数组转为Class对象。

defineClass

defineClass主要的功能是:将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。如,假设class文件是加密过的,则需要解密后作为形参传入defineClass函数。

defineClass默认实现如下:

protected final Class<?> defineClass(String name, byte[] b, int off, int len)throws ClassFormatError  {return defineClass(name, b, off, len, null);
}

函数调用过程

函数调用过程,如下图所示:

通常情况下,我们都是直接使用系统类加载器。但是,有的时候,我们也需要自定义类加载器。比如应用是通过网络来传输 Java类的字节码,为保证安全性,这些字节码经过了加密处理,这时系统类加载器就无法对其进行加载,这样则需要自定义类加载器来实现。自定义类加载器一般都是继承自ClassLoader类,从上面对loadClass方法来分析来看,我们只需要重写 findClass 方法即可。下面我们通过一个示例来演示自定义类加载器的流程:

package com.xttblog.classloader;
import java.io.*;
public class MyClassLoader extends ClassLoader {private String root;// 业余草:www.xttblog.comprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] classData = loadClassData(name);if (classData == null) {throw new ClassNotFoundException();} else {return defineClass(name, classData, 0, classData.length);}}private byte[] loadClassData(String className) {String fileName = root + File.separatorChar+ className.replace('.', File.separatorChar) + ".class";try {InputStream ins = new FileInputStream(fileName);ByteArrayOutputStream baos = new ByteArrayOutputStream();int bufferSize = 1024;byte[] buffer = new byte[bufferSize];int length = 0;while ((length = ins.read(buffer)) != -1) {baos.write(buffer, 0, length);}return baos.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;}public String getRoot() {return root;}public void setRoot(String root) {this.root = root;}public static void main(String[] args)  {MyClassLoader classLoader = new MyClassLoader();classLoader.setRoot("E:\\temp");Class<?> testClass = null;try {testClass = classLoader.loadClass("com.xttblog.classloader.Test2");Object object = testClass.newInstance();System.out.println(object.getClass().getClassLoader());} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
}

自定义类加载器的核心在于对字节码文件的获取,如果是加密的字节码则需要在该类中对文件进行解密。由于这里只是演示,我并未对class文件进行加密,因此没有解密的过程。这里有几点需要注意:

  1. 这里传递的文件名需要是类的全限定性名称,即com.paddx.test.classloading.Test格式的,因为 defineClass 方法是按这种格式进行处理的。
  2. 最好不要重写loadClass方法,因为这样容易破坏双亲委托模式。
  3. 这类Test 类本身可以被AppClassLoader类加载,因此我们不能把com/paddx/test/classloading/Test.class放在类路径下。否则,由于双亲委托机制的存在,会直接导致该类由AppClassLoader加载,而不会通过我们自定义类加载器来加载。

Java 自定义类加载器教程相关推荐

  1. java自定义类加载器

    import java.io.*;public class MyClassLoader extends ClassLoader{private String loaderName; //类加载器名称p ...

  2. java 自定义类加载器_Java-JVM 自定义类加载器

    一.sun.misc.Launcher (ExtClassLoader 与 AppClassLoader 的创建) publicLauncher() { Launcher.ExtClassLoader ...

  3. java自定义类加载器与实现类加载器加密与解密

    创建自定义加载器的类 package com.tuogo.classlocad;import java.io.ByteArrayOutputStream; import java.io.FileInp ...

  4. java中的类加载器有,Java自定义的类加载器,java自定义加载,在java中类加载器有以...

    Java自定义的类加载器,java自定义加载,在java中类加载器有以 在java中类加载器有以下几种java虚拟机自带的加载器 1)根类加载器(Bootstrap,c++实现)2)扩展类加载器(Ex ...

  5. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  6. java 扩展类加载器_java实现自定义类加载器

    各类加载器虽然以父子相称,但是没有继承关系 (视频教程推荐:java课程) 点入ClassLoader的源码查看样例:* * class NetworkClassLoader extends Clas ...

  7. java简单通讯录的实现02person类_Java自定义类加载器实现不同版本的类加载

    一 什么是类隔离技术 只要你 Java 代码写的足够多,就一定会出现这种情况:系统新引入了一个中间件的 jar 包,编译的时候一切正常,一运行就报错:java.lang.NoSuchMethodErr ...

  8. java import自定义类_Java实现的自定义类加载器示例

    本文实例讲述了Java实现的自定义类加载器.分享给大家供大家参考,具体如下: 一 点睛 1 ClassLoader类有如下两个关键方法: loadClass(String name, boolean ...

  9. 28 Java类的加载机制、什么是类的加载、类的生命周期、加载:查找并加载类的二进制数据、连接、初始化、类加载器、双亲委派模型、自定义类加载器

    28Java类的加载机制 28.1.什么是类的加载 28.2.类的生命周期 28.2.1.加载:查找并加载类的二进制数据 28.2.2.连接 28.2.3.初始化 28.3.类加载器 28.4.类的加 ...

最新文章

  1. 四柱加强版汉诺塔HanoiTower----是甜蜜还是烦恼
  2. Mongodb 集群加keyFile认证,Mongodb用户管理(转:http://blog.csdn.net/wlzjsj/article/details/61421230)
  3. 通过java.net.URLConnection发送HTTP请求的方法
  4. 【渝粤教育】国家开放大学2018年秋季 0554-21T立体构成(一) 参考试题
  5. 一件毛衣能产生7000伏电压!冬天静电的危害竟然这么大…
  6. Tech.Ed上海会场都结束了!
  7. python random
  8. mysql 语句中的national 、 comment
  9. OpenGL基础6:着色器
  10. 高可用Redis(四):列表,集合与有序集合
  11. select和epoll
  12. 2022年前端还好找工作吗?
  13. 苹果HomeKit生态深度解析,在智能家居领域后发制人?
  14. win10安装过程修改esp分区吗_win7/win10无损修改UEFI启动模式让系统5秒开机支持ghost版...
  15. 到底什么是QPS、TPS、RT、PV、UV、IV、VV、IP、系统吞吐量?
  16. 训练过程出现trian_dice一直大于1(mask范围0-255转为0-1)
  17. Python(贪心算法)问题 D: 删数问题_输入一个高精度的正整数N,去掉其中任意S个数字后剩下的数字按原左右次序组成一个新的正整数。
  18. 【web性能】获取web各个阶段响应时间:服务器响应时间、首页白屏时间、dom渲染完成时间等
  19. windows界面-python-运动倒计时程序
  20. CSS进阶(一)背景与边框

热门文章

  1. html5 audio设置currentTime
  2. android gps源码分析,Android编程之Android GPS ——AGPS源码分析及配置
  3. Linux——Linux系统编程之串口编程总结(串口的初始化、读写操作实践)
  4. 鼠标的光标不随鼠标移动问题的可能性分析
  5. 如何给网站注入木马程序
  6. 计算机显示器上有条纹,电脑显示器出现条纹的解决参考
  7. EasyExcel-----使用篇(读取Excel)
  8. 补码100000000,原反补的那些事儿!
  9. C++ 广度优先算法
  10. Matlab:可视化标量三维体数据的方法