类加载器的继承层次
java类加载器按照如下图所示的结构组织,各加载器各司其职只加载自己管辖范围内的类。
引导类加载器(Bootstrap):java虚拟机内置的加载器,在虚拟机启动的时候会用这个类加载器来加载 JDK安装目录下的 /JRE/LIB/rt.jar ,也就是系统默认导入的一些类(如下图所示)。不能通过代码直接获取引导类加载器的引用,获取的都是null。
扩展类加载器(ExtClassLoader):这个类加载器加载JDK安装目录下的/JRE/LIB/ext 目录中的类:
系统类加载器(AppClassLoader):根据java程序的classpath加载对应的类。可以通过ClassLoader.getSystemClassLoader()来取得。
public class Demo { public static void main(String[] args) { //系统类加载器 ClassLoader demoClassLoader = Demo.class.getClassLoader(); System.out.println(demoClassLoader); //sun.misc.Launcher$AppClassLoader@5e87512 //扩展类加载器 ClassLoader demoClassLoaderParent = demoClassLoader.getParent(); System.out.println(demoClassLoaderParent); //sun.misc.Launcher$ExtClassLoader@605df3c5 //引导类加载器 ClassLoader objectClassLoader = Object.class.getClassLoader(); System.out.println(objectClassLoader); //null System.out.println(demoClassLoaderParent.getParent()); //null } }
JVM判断两个类相同的准则
只有当类的全限定名相同且加载此类的类加载器(类的定义加载器)相同,jvm才认为两个类相同。不同类加载器加载的类之间不兼容,类A的同一份A.class文件被ClassLoaderA和ClassLoaderB加载后会定义出两个表示类A的java.lang.Class实例,这两个实例是不同的。
代理加载类
如果由用户自定义的类加载器来加载核心库,那么系统中同一个核心类就可能存在多个版本,互相之间不兼容。如果加载核心类的工作交由引导类加载器来完成,就不会出现多版本的问题。试想com.lang.Object由两个不同的类加载器加载,则jvm中存在两个表征Object的Class,因此可能实例化出两种Object对象,但是彼此不相等,terrible! 另一个问题,如果用户自定义java.lang.Object类,且用自定义的类加载器来加载,那么核心库提供的类就被屏蔽了,同样用户也可以对核心库进行任意修改,这都将导致java核心库处于不安全的状态。为了解决这些问题,java类加载器使用向上代理的方式加载类,即发起加载任务的类加载器优先将加载任务代理给其父类加载器,如果父类加载器加载不到就继续代理给上层类加载器,直至引导类加载器,如果还是未加载到,最初发起加载任务的类加载器就自己加载类,这样核心库的类只会被jvm指定的类加载器加载,自定义的java.lang.Object类永远不会被加载。类加载器工作的核心源码如下
类加载器优先将加载任务代理给其父类,所以真正完成加载任务的加载器(类的定义加载器)和发起加载任务的加载器( 初始加载器 )可能不是同一个。类的定义加载器才是jvm判断两个类是否相同的一个准则。如下图, 类A的类定义加载器会发起类B的加载,因此类A的类定义加载器是类B的初始化加载器。
类的初始加载器通过调用loadClass( ) 来启动类加载,此过程可能抛出java.lang.ClassNotFoundException,类的定义加载器通过调用其defineClass( )来加载类,此过程可能抛出java.lang.NoClassDefFoundError。类加载器在成功加载某个类之后,会把得到的 java.lang.Class
类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载,也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即loadClass()
方法不会被重复调用。每个Class对象对保留有对定义他的类加载器的引用,通过对类调用getClassloader( )来获取该引用。
当自定义类加载器时,为了保证委托机制,建议重写 findClass(String binaryName) 而不是重写loadClass(String binaryName,Boolean resolve)。重写findClass主要是实现另一种获取字节码的方式。
线程上下文类加载器
java提供了很多服务提供者接口(Servie Provider Interface),例如jdbc、jndi、jaxp等,这些spi接口定义在java核心库中,但是spi的实现(服务提供者)由第三方提供(例如mysql提供mysql数据库驱动,oralce提供oracle提供oracle的驱动),以应用依赖的jar的形式存在,接口由引导类加载器加载,实现由系统类加载器加载,因为引导类加载器(加载器的鼻祖)只加载java核心库,无法向下代理给系统类加载器加载spi的实现类,因此在spi接口中无法加载到spi的实现。怎么解决这个问题呢?为了解决这个问题,java提供了线程上下文类加载器,java应用程序的主线程上下文类加载器是系统类加载,子线程默认继承父线程的上下文的类加载器。不同与向上代理的模式,线程上下文类类加载器相当于父类加载器委托子类加载器代理加载父类加载不到的类。有了线程上下文类加载器,引导类加载器就可以委托它为spi加载相应的实现。可以通过Thread.currentThread().getContextClassLoader( )类获得当前线程的上下文类加载器。
自定义类加载器
自定义类加载器通常是为了通过特殊途径获取字节码或者对字节码进行特殊处理,通常只需要重写Class<?> findClass(String name) 方法即可。以下demo通过自定义列加载器来加载经过DES加密算法加密后的字节码文件,生成相应的类。public class MatrixClassLoader extends ClassLoader { //字节码文件路径 private String clazzFilePath; MatrixClassLoader(String clazzFilePath){ this.clazzFilePath = clazzFilePath; } /** * 重写findClass(String name),实现自己的获取字节码的方式 。 * 此处通过des解密算法对加密的字节码文件解密,并返回解密后的字节码文件的字节数组 */ @Override protected Class<?> findClass(String name) { byte[] clazzByte = null; try { DES des = new DES("123"); //用123解密字节码,用字节数据返回解密后的字节码数据 clazzByte = des.decryp(clazzFilePath); return defineClass(name, clazzByte, 0, clazzByte.length); //将字节码数据交给ClassLoader处理 } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { String path = "C:/Users/Administrator/Desktop/bin/Clazz.class"; //加密前的字节码文件 DES des = new DES("123"); //用123加密字节码,返回加密文件的路径 String encryptedFilePath =des.encryFile(path); MatrixClassLoader classLoader = new MatrixClassLoader(encryptedFilePath); Class clazz = classLoader.loadClass("com.hsh.cl.Clazz"); } }
相关推荐
类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java ...
看完一个Java加载原理教程后,写了这个自己的类加载器,作个笔记,以便以且使用
自定义类加载器实现自定义加载。自定义类加载器实现自定义加载
DevLoader.zip tomcat 类加载器
Java类加载器可以直接从Maven存储库加载并运行类,能在运行时解决依赖关系
当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构,理解类加载器:J2EE 环境下的 log4j.files
简单的自定义类加载器问候世界hello word,基于磁盘的ClassLoader
类装载器学习一、类加载器的基本概念 类装载器学习一、类加载器的基本概念 类装载器学习一、类加载器的基本概念
java 类加密 使用类加载器解密加载类 反射执行main
类加载器
ClassLoader 三种类加载方式 Boostrap Extenxsion 以及Application ClassLoad分别适用的场景
JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK)。 SE(J2SE),standard edition,标准版,是我们通常用的一个版本,从JDK 5.0开始,改名为Java SE。
java应用程序类加载器(ClassLoader for java Application),类似exe4j, 方便启动java程序, 配置灵活,支持多平台选择性配置
(父子关系一般不会以继承的关系实现,而是以组合关系来复用父加载器的代码)工作过程如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请
什么是类加载器和什么是双亲委派机制
ClassLoader的API使用和自定义
类加载器是Java最强大的特征之一。但是开发者常常忘记类加载组件。类加载器是在运行时负责寻找和加载类文件的类。Java允许使用不同的类加载器,甚至自定义的类加载器。类加载器从源文件(通常是.class 或 .jar文件)...
3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4
java类加载器学习三、类加载器的委托模式