定义

前面说过加载阶段是一个可以让设计人员高度自控的模块,因为类文件的源头可以是多种多样的,代码生成、反射生成或从网络中生成等。因此类加载器作为对这些文件的处理就显得尤为重要。

但类加载器的功能不仅如此,其还有一个重要的功能就是和一个类的全限定名唯一确定一个类。通俗来说,要说两个类是相同的,不仅其全限定名要一样,其对应的类加载器也必须相同,才能说明两个类是相等的。

正因为类加载器的功能角色如此重要,因此虚拟机对其的实现规范也十分重视。在Java虚拟机中,对其的实现模型是双亲委派模型

模型

双亲委派模型的主要执行过程示意图如上所示,其分为启动类加载器(Bootstrap Class-loader),拓展类加载器(Extension Class-loader),应用程序类加载(Application Class-loader)。

其中启动类加载器主要负责加载 JRE 的核心类库,如 JRE 目录下的 rt.jar。但其实根据《深入分析 Java Web 技术内幕》上所说,启动类加载器并不严格符合双亲委派模型,因为Bootstrap Class-loader 并不属于 JVM 的类等级层次。Bootstrap Class-loader 是没有子类的,Extension Class-loader 也是没有父类的。不过在这里我们并不深究,只要知道有这一点就可以了。

Extension Class-loader 主要负责加载 JRE 拓展目录 ext 下的类。

Application Class-loader 主要负责用户类路径(Class-path)下的类,这个类加载器是使用的最多的,因为大大多数情况下,一般开发者并没有实现自定义的类加载器,那么 JVM 就会使用这个来加载类大部分类。

执行过程

上图就是双亲委派模型的执行过程,当类开始加载的时候,先检查是否已经被加载过,如果没有被加载过,则调用父类的加载方法,如果父类加载失败,抛出异常,则调用自身的 findClass() 方法进行加载。

JDK 中加载过程的源码分析:

protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First,check if the class has already been loaded
// 如果加载过了,就不要加载直接返回
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 判断是否有父加载器
if (parent != null) {
// 有父加载器则调用父加载器加载
c = parent.loadClass(name, false);
} else {
// 无父亲就调用 bootstarp 加载器来加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 父加载器和 bootstarp 加载器都没有找到指定类,调用当前类的 findClass() 来完成类加载
// 因此,自定义类加载器,就是重写 findClass() 方法
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 stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

从源码中,我们可以看到其实符合规范要求的 双亲委派模型 的。而当我们要自定义一个类加载器的时候就是通过重写 findClass() 来实现的。

自定义类加载器

/**
* 1. 自定义类加载器通过集成ClassLoader来实现,主要通过重写findClass方法
* 2. findClass方法首先通过自定义的loadByte()方法将Class文件转换成byte[]字节流
* 3. 然后通过defineClass()方法将其转换为Class对象
*/
public class SelfClassLoader extends ClassLoader {
private String classPath;
public SelfClassLoader(String classPath) {
this.classPath = classPath;
} /**
* 通过 difineClass,将一个字节数组转换为Class 对象
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
} /**
* 根据路径将指定的文件读取为byte 流
* @param name
* @return
* @throws IOException
*/
private byte[] loadByte(String name) throws IOException {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name
+ ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
}

另一个种实现自定义类加载器的方法:

/**
* 1. 加载指定packageName下的类
* 2. 用自定义类加载器进行加载,如果加载失败,再交给父加载器进行加载
*/
public class UrlSelfClassloader extends URLClassLoader {
private String packageName = ""; public UrlSelfClassloader(URL[] urls, ClassLoader parent) {
super(urls, parent);
} @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> aClass = findLoadedClass(name);
if (Objects.nonNull(aClass)){
return aClass;
}
if (!packageName.startsWith(name)){
return super.loadClass(name);
}else {
return findClass(name);
}
}
}

如何使用自定义的类加载器

public static void main(String args[]) throws Exception {
MyClassLoader classLoader = new MyClassLoader("");
Class clazz = classLoader.loadClass("");
Object obj = clazz.newInstance();
Method helloMethod = clazz.getDeclaredMethod("hello", null);
helloMethod.invoke(obj, null);
}

[JVM-6]类加载器的更多相关文章

  1. JVM自定义类加载器加载指定classPath下的所有class及jar

    一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...

  2. 【深入理解JVM】类加载器与双亲委派模型 (转)

    出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...

  3. 1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  4. 【JVM】JVM之类加载器

    一.前言 首先,小小测试,看是否已经掌握了JVM类加载的过程 1.1.测试一 class Singleton { private static Singleton sin = new Singleto ...

  5. JVM之类加载器下篇

    除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载. --启动类加载器,又称根加载器,是一个native的方法,使用c++实现.在java中我们用null标识,用于加载j ...

  6. 【深入理解JVM】类加载器与双亲委派模型

    原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...

  7. JVM学习一:JVM之类加载器概况

    18年转眼就3月份都快结束了,也就是说一个季度就结束了:而我也因为年前笔记本坏了,今天刚修好了,那么也应该继续学习和博客之旅了.今年的博客之旅,从JVM开始学起,下面我们就言归正传,进入正题. 一.J ...

  8. (转)JVM——自定义类加载器

    背景:为什么要自定义,如何自定义,实现过程 转载:http://blog.csdn.net/SEU_Calvin/article/details/52315125 0. 为什么需要自定义类加载器 网上 ...

  9. JVM虚拟机-类加载器子系统

    转自博客:http://www.cnblogs.com/muffe/p/3541189.html   还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...

  10. 深入理解JVM一类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

随机推荐

  1. vue ele 表单规则校验俩次输入密码是否相同,校验手机号 ( 前端小课堂:小细节,大进步 )

     这个是密码的拦截 : [{ required: true, validator: validatePass4, trigger: "blur" }],   同级关系下写下方法,类 ...

  2. WebService学习总结(二)--使用JDK开发WebService

    一.WebService的开发方法 使用java的WebService时可以使用一下两种开发手段 使用jdk开发(1.6及以上版本) 使用CXF框架开发(工作中) 二.使用JDK开发WebServic ...

  3. iMX6UL配置MCP2515模块(SPI转CAN)——基于迅为iTOP-iMX6UL开发板

    写在前面   在文章"嵌入式Linux的CAN总线配置--基于迅为iTOP-4412开发板"中我给4412开发板配置了SPI转CAN模块,使用的是不带设备树的内核.在本篇文章中,要 ...

  4. Linux - centos7.X 安裝 Python 3.7

    说明 全部操作都在 root 用户下执行 安装编译相关工具 yum -y groupinstall "Development tools" yum -y install zlib- ...

  5. @RequestParam、@RequestBody、@PathVariable区别和案例分析

    一.前言 @RequestParam.@RequestBody.@PathVariable都是用于在Controller层接收前端传递的数据,他们之间的使用场景不太一样,今天来介绍一下!! 二.实体类 ...

  6. Sonarqube C# 配置资料整理

    c#配置方式: http://www.cnblogs.com/CoderAyu/p/9416376.html http://www.cnblogs.com/jingridong/p/6513884.h ...

  7. 傻子都能懂的并查集题解——HDU1232畅通工程

    原题内容: Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都 ...

  8. 使用Java MVC模式设计一个学生管理系统

    最近在做web实验,要求是用jsp+servlet+mysql实现一个学生管理系统,完成对数据库的增删改查. 效果图:   代码: package dao; import java.util.List ...

  9. PHP中的国际化日历类

    在 PHP 的国际化组件中,还有一个我们并不是很常用的跟日期相关的操作类,它就是日历操作类.说是日历,其实大部分还是对日期时间的操作,一般也是主要用于日期的格式化和比较之类的.但是通常我们直接使用 d ...

  10. Java基础系列(3)- HelloWorld详解

    HelloWorld 1.新建一个java文件 文件后缀名为.java Hello.java [注意点]系统可能没有显示文件后缀名,我们需要手动打开 2.编写代码 public class Hello ...