1.类加载机制
类加载器ClassLoader
- 负责查找、加载、校验字节码的应用程序
- java.lang.ClassLoader
- load(String className) 根据名字加载一个类,返回类的实例
- defineClass(String name, byte[] b, int off, int len) 将一个字节流定义一个类
- findClass(String name) 查找一个类
- findLoadedClass(String name) 在已加载的类中,查找一个类
- 成员变量 ClassLoader parent
JVM四级类加载器
- 启动类加载器(Bootstrap),系统类rt.jar
- 扩展类加载器(Extension),jre/lib/ext
- 应用类加载器(App),classpath
- 用户自定义加载器(Plugin),程序自定义
类加载器双亲委托
- 首先判断是否已经加载
- 若无,找父加载器加载
- 若再无,由当前加载器加载
2.双亲委托加载扩展
- Java严格执行双亲委托机制
- 类会由最顶层的加载器来加载,如没有,才由下级加载器加载
- 委托是单向的,确保上层核心的类的正确性
- 但是上级类加载器所加载的类,无法访问下级类加载器所加载的类
- 例如,java.lang.String 无法访问自定义的一个Test类
- Java是一个遵循契约设计的程序语言,核心类库提供接口,应用层提供实现
- 核心类库是BootstrapClassLoader加载
- 应用层是AppClassLoader加载
- 典型例子是JDBC和XML Parser等
双亲委托的补充
- 执行Java,添加虚拟机参数-Xbootclasspath/a:path,将类路径配置为Bootstrap等级
- 使用ServiceLoader.load方法,来加载(底层加载器所加载的类)
ServiceLoader
- JDK 6引入的一种新特性,是用于加载服务的一种工具
- 服务有接口定义和具体的实现类(服务提供者)
- SPI机制,Service Provider Interface
- 一个服务提供者会在jar包中有META-INF/services目录,里面放一个文件,名字同接口名字。内容的每一行都是接口的一个实现类
- load方法,可以用当前线程的类加载器来获取某接口的所有实现,当然也都是转为接口类来使用
- 注意:此服务和Java 9模块系统的服务略有差别,但是都能通过ServiceLoader进行加载
3.自定义类加载路径
自定义类加载器
- 自定义加载路径
- 弥补类搜索路径静态的不足
- URLClassLoader, 从多个URL(jar或者目录)中加载类
- 自定义类加载器
- 继承ClassLoader类
- 重写findClass(String className)方法
自定义加载路径
- 弥补类搜索路径静态的不足
- 前3个加载的路径都是运行前确定的
- Java提供URLClassLoader
- 程序运行时修改类的加载路径
- 可从多个来源中加载类
URLClassLoader
- 继承于ClassLoader
- 程序运行时增加新的类加载路径
- 可从多个来源中加载类
- 目录
- jar包
- 网络
- addURL添加路径
- close方法关闭
1 | //URL支持http, https, file, jar 四种协议 |
4.自定义类加载器
- 继承ClassLoader类
- 重写findClass(String className)方法
- 使用时,默认先调用loadClass(className)来查看是否已经加载过,然后委托双亲加载,如果都没有,再通过findClass加载返回
- 在findClass中,首先读取字节码文件
- 然后,调用defineClass(className, bytes, off, len) 将类注册到虚拟机中
- 可以重写loadClass方法来突破双亲加载
- 同一个类可以被不同层级的加载器加载,且作为2个类对待
1 | class CryptoClassLoader extends ClassLoader |