为何要花时间实现自己的ClassLoader

尽管人生的乐趣非常大一部分来自于将时间花在有意思可是无意义的事情上,可是这件事绝对是有意思并且有意义的,有下面几个情景是值得我们花费时间实现自己的classLoader的:

  • 我们须要的类不一定存放在已经设置好的classPath下(有系统类载入器AppClassLoader载入的路径),对于自己定义路径中的class类文件的载入,我们须要自己的ClassLoader
  • 有时我们不一定是从类文件里读取类,可能是从网络的输入流中读取类,这就须要做一些加密和解密操作,这就须要自己实现载入类的逻辑,当然其它的特殊处理也相同适用。
  • 能够定义类的实现机制。实现类的热部署,如OSGi中的bundle模块就是通过实现自己的ClassLoader实现的。

理解ClassLoader类的结构

载入class文件

ClassLoader的loadClass採用双亲托付型实现。由于我们实现的ClassLoader都继承于java.lang.ClassLoader类,父载入器都是AppClassLoader。所以在上层逻辑中依然要保证该模型,所以一般不覆盖loadClass函数

protected synchronized Class<?> loadClass ( String name , boolean resolve ) throws ClassNotFoundException{
//检查指定类是否被当前类载入器载入过
Class c = findLoadedClass(name);
if( c == null ){//假设没被载入过。委派给父载入器载入
try{
if( parent != null )
c = parent.loadClass(name,resolve);
else
c = findBootstrapClassOrNull(name);
}catch ( ClassNotFoundException e ){
//假设父载入器无法载入
}
if( c == null ){//父类不能载入,由当前的类载入器载入
c = findClass(name);
}
}
if( resolve ){//假设要求马上链接,那么载入完类直接链接
resolveClass();
}
//将载入过这个类对象直接返回
return c;
}

从上面的代码中。我们能够看到在父载入器不能完毕载入任务时,会调用findClass(name)函数,这个就是我们自己实现的ClassLoader的查找类文件的规则。所以在继承后。我们仅仅须要覆盖findClass()这个函数,实现我们在本载入器中的查找逻辑,并且还不会破坏双亲托付模型

载入资源文件(URL)

我们有时会用Class.getResource():URL来获取对应的资源文件。假设仅仅使用上面的ClassLoader是找不到这个资源的,对应的返回值为null。

下面我们来看Class.getResource()的源代码:

public java.net.URL getResource(String name) {
name = resolveName(name);//解析资源
ClassLoader cl = getClassLoader();//获取到当前类的classLoader
if (cl==null) {//假设为空,那么利用系统类载入器载入
// A system class.
return ClassLoader.getSystemResource(name);
}
//假设获取到classLoader,利用指定的classLoader载入资源
return cl.getResource(name);
}

我们发现Class.getResource()是通过托付给ClassLoader的getResource()实现的,所以我们来看classLoader对于资源文件的获取的详细实现例如以下:

    public URL getResource(String name) {
URL url;
if (parent != null) {
url = parent.getResource(name);
} else {
url = getBootstrapResource(name);
}
if (url == null) {
url = findResource(name);//这里
}
return url;
}

通过代码我们easy发现。也是双亲委派模型的实现,在不破坏模型的前提下,我们发现我们须要覆写的仅仅是findResource(name)函数

综上

我们在创建自己的ClassLoader时仅仅须要覆写findClass(name)和findResource()就可以

例讲ClassLoader的实现

下面的实现均基于对于ClassLoader抽象类的继承(仅仅给出对于findClass的覆写。由于常理上处理逻辑基本一致)

载入自己定义路径下的class文件

package com.company;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.ByteBuffer; /**
* Created by liulin on 16-4-20.
*/
public class MyClassLoader extends ClassLoader {
private String classpath; public MyClassLoader( String classpath){
this.classpath = classpath;
} @Override
protected Class<? > findClass(String name) throws ClassNotFoundException {
String fileName = getClassFile( name );
byte[] classByte=null;
try {
classByte = getClassBytes(fileName);
}catch( IOException e ){
e.printStackTrace();
}
//利用自身的载入器载入类
Class retClass = defineClass( null,classByte , 0 , classByte.length);
if( retClass != null ) {
System.out.println("由我载入");
return retClass;
}
//System.out.println("非我载入");
//在classPath中找不到类文件,托付给父载入器载入,父类会返回null,由于可载入的话在
//委派的过程中就已经被载入了
return super.findClass(name);
} /***
* 获取指定类文件的字节数组
* @param name
* @return 类文件的字节数组
* @throws IOException
*/
private byte [] getClassBytes ( String name ) throws IOException{
FileInputStream fileInput = new FileInputStream(name);
FileChannel channel = fileInput.getChannel();
ByteArrayOutputStream output = new ByteArrayOutputStream();
WritableByteChannel byteChannel = Channels.newChannel(output);
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
int flag;
while ((flag = channel.read(buffer)) != -1) {
if (flag == 0) break;
//将buffer写入byteChannel
buffer.flip();
byteChannel.write(buffer);
buffer.clear();
}
}catch ( IOException e ){
System.out.println("can't read!");
throw e;
}
fileInput.close();
channel.close();
byteChannel.close();
return output.toByteArray();
} /***
* 获取当前操作系统下的类文件合法路径
* @param name
* @return 合法的路径文件名称
*/
private String getClassFile ( String name ){
//利用StringBuilder将包形式的类名转化为Unix形式的路径
StringBuilder sb = new StringBuilder(classpath);
sb.append("/")
.append ( name.replace('.','/'))
.append(".class");
return sb.toString();
} public static void main ( String [] args ) throws ClassNotFoundException {
MyClassLoader myClassLoader = new MyClassLoader("/home/liulin/byj");
try {
myClassLoader.loadClass("java.io.InputStream");
myClassLoader.loadClass("TestServer");
myClassLoader.loadClass("noClass");
}catch ( ClassNotFoundException e ){
e.printStackTrace();
}
}
}

结果例如以下:



从结果我们看,由于我们载入的类的父载入器是系统载入器,所以调用双亲托付的loadClass,会直接载入掉java.io.InputStream类。仅仅有在载入双亲中没有的TestServer类,才会用到我们自己的findClass载入逻辑载入指定路径下的类文件,满足双亲委派模型详细前面已经讲述过。不再赘述

热部署和加密解密的ClassLoader实现,大同小异。

仅仅是findClass的逻辑发生改变而已

JVM基础(二) 实现自己的ClassLoader的更多相关文章

  1. 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】

    目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...

  2. JVM 基础知识

    JVM 基础知识(GC) 2013-12-10 00:16 3190人阅读 评论(1) 收藏 举报 分类: Java(49) 目录(?)[+] 几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看 ...

  3. JVM(二)Java虚拟机组成详解

    导读:详细而深入的总结,是对知识"豁然开朗"之后的"刻骨铭心",想忘记都难. Java虚拟机(Java Virtual Machine)下文简称jvm,上一篇我 ...

  4. Java面试题总结之Java基础(二)

    Java面试题总结之Java基础(二) 1.写clone()方法时,通常都有一行代码,是什么? 答:super.clone(),他负责产生正确大小的空间,并逐位复制. 2.GC 是什么? 为什么要有G ...

  5. JVM基础系列第15讲:JDK性能监控命令

    查看虚拟机进程:jps 命令 jps 命令可以列出所有的 Java 进程.如果 jps 不加任何参数,可以列出 Java 程序的进程 ID 以及 Main 函数短名称,如下所示. $ jps 6540 ...

  6. 你的 JVM 基础“大厦”稳健吗?

    [从 1 开始学 JVM 系列] JVM 对于每位 Java 语言编程者来说无疑是"重中之重",尽管我们每天都在与它打交道,却很少来审视它.了解它,慢慢地,它成为了我们" ...

  7. Python全栈开发【基础二】

    Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...

  8. Bootstrap <基础二十九>面板(Panels)

    Bootstrap 面板(Panels).面板组件用于把 DOM 组件插入到一个盒子中.创建一个基本的面板,只需要向 <div> 元素添加 class .panel 和 class .pa ...

  9. Bootstrap <基础二十八>列表组

    列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...

随机推荐

  1. web认证方案

    web构建在http之上,而它又是无状态协议,如何控制用户访问服务器上的受限资源呢? 最原始你想法通过http基本认证,每次发请求时都向后台传递用户名密码信息,服务器每次收到请求后都先验证用户是否合法 ...

  2. im4java+GraphicsMagick

    package com.jeeplus.modules.isp.utils; import java.io.ByteArrayInputStream; import java.io.ByteArray ...

  3. mygenerator().next() AttributeError: 'generator' object has no attribute 'next'

    def mygenerator(): print ("start ...") yield 5 mygenerator() print ("mygenerator():&q ...

  4. HBase的集群搭建(1、3、5节点都适用)

    见 5 hbase-shell + hbase的java api

  5. C# html生成PDF遇到的问题,从iTextSharp到wkhtmltopdf

    我们的网站业务会生成一个报告,用网页展示出来,要有生成pdf并下载的功能,关键是生成pdf. 用内容一段段去拼pdf,想想就很崩溃,所以就去网上找直接把html生成pdf的方法. 网上资料大部分都是用 ...

  6. iOS - CocoaPods操作详解

    在我们进行iOS应用开发的时候,肯定会用到很多的第三方类库,最常用AFNetworking,SDWebImage等等,当我们用到这个类库的时候,就要一个一个的去下载这个类库,如果这个类库中又用到了其他 ...

  7. 【PL/SQL】匿名块、存储过程、函数、触发器

    名词解释 子程序:PL/SQL的过程和函数统称为子程序. 匿名块:以DECLARE或BEGIN开始,每次提交都被编译.匿名块因为没有名称,所以不能在数据库中存储并且不能直接从其他PL/SQL块中调用. ...

  8. XML、集合、JSP综合练习

    一.利用DOM解析XML文件得到信息:存入泛型集合中在JSP页面循环打印读取的信息 a)         编写XML文件:添加测试节点数据 b)         建立web项目:在JSP页面中使用DO ...

  9. 2星|《工业X.0》:物联网的资料汇编

    工业X.0:实现工业领域数字价值 看完比较失望,没有看到新的观点想法.基本算是物联网的资料汇编.总体评价2星. 以下是书中一些内容的摘抄: 1:例如,埃森哲为其员工开发了一个用例,用增强现实技术解决实 ...

  10. 分布式机器学习框架:CXXNet

    caffe是很优秀的dl平台.影响了后面很多相关框架.        cxxnet借鉴了很多caffe的思想.相比之下,cxxnet在实现上更加干净,例如依赖很少,通过mshadow的模板化使得gpu ...