JVM基础(二) 实现自己的ClassLoader
为何要花时间实现自己的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的更多相关文章
- 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...
- JVM 基础知识
JVM 基础知识(GC) 2013-12-10 00:16 3190人阅读 评论(1) 收藏 举报 分类: Java(49) 目录(?)[+] 几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看 ...
- JVM(二)Java虚拟机组成详解
导读:详细而深入的总结,是对知识"豁然开朗"之后的"刻骨铭心",想忘记都难. Java虚拟机(Java Virtual Machine)下文简称jvm,上一篇我 ...
- Java面试题总结之Java基础(二)
Java面试题总结之Java基础(二) 1.写clone()方法时,通常都有一行代码,是什么? 答:super.clone(),他负责产生正确大小的空间,并逐位复制. 2.GC 是什么? 为什么要有G ...
- JVM基础系列第15讲:JDK性能监控命令
查看虚拟机进程:jps 命令 jps 命令可以列出所有的 Java 进程.如果 jps 不加任何参数,可以列出 Java 程序的进程 ID 以及 Main 函数短名称,如下所示. $ jps 6540 ...
- 你的 JVM 基础“大厦”稳健吗?
[从 1 开始学 JVM 系列] JVM 对于每位 Java 语言编程者来说无疑是"重中之重",尽管我们每天都在与它打交道,却很少来审视它.了解它,慢慢地,它成为了我们" ...
- Python全栈开发【基础二】
Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...
- Bootstrap <基础二十九>面板(Panels)
Bootstrap 面板(Panels).面板组件用于把 DOM 组件插入到一个盒子中.创建一个基本的面板,只需要向 <div> 元素添加 class .panel 和 class .pa ...
- Bootstrap <基础二十八>列表组
列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...
随机推荐
- Java-MyBatis:MyBatis
ylbtech-Java-MyBatis:MyBatis 1.返回顶部 1. MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foun ...
- codevs2800送外卖(floyd+状压dp)
2800 送外卖 时间限制: 2 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 有一个送外卖的,他手上有n份订单,他要把n份东 ...
- C语言和C++的应用领域都在哪些?学C语言好,还是学习C++好?
从事嵌入式开发十几年,基本上围绕着这两种编程语言展开,都可以直接操作底层的编程语言,用的越熟练越是感觉工具属性越强.虽然两种编程语言分属于不同的编程思想,用的时间长了觉得差异也不是很大,现在就个人的从 ...
- netty百万连接跟踪记录
0. 启动客户端和服务端 # 测试环境: centos7 jdk8 2核16G# 服务端启动nohup java -Xmx8192m -Xms4096m -XX:+UseG1GC -XX:Parall ...
- SQL Server中char与varchar数据类型区别
在SQL Server中char类型的长度是不可变的,而varchar的长度是可变的 . 存入数据时: 如果数据类型为char时,当定义一个字段固定长度时,如果存进去数据长度小于char的长度,那么存 ...
- WEB笔记-2 剖析CSS规则
2.1 剖析CSS规则 规则即指令,其声明了需要修改的元素及要应用给元素的样式. 2.2 为文档添加样式的三种方法 行内样式:直接写在HTML文档标签中的style属性当中,行内元素只 ...
- vsftp服务器搭建
1.FTP的主动模式和被动模式的区别: 最大的区别是数据端口并不总是20, 主动模式和被动模式的优缺点: 主动FTP对FTP服务器的管理和安全很有利,但对客户端的管理不利.因为FTP服务器企图与客户端 ...
- 【JSP】常用跳转方式
原文地址:http://blog.csdn.net/wanghuan203/article/details/8836326 (1)href超链接标记,属于客户端跳转 (2)使用javascript完成 ...
- mysql手册操作
1.show table status 显示表状态 2.VERSION() 版本:CURRENT_DATE 当前日期: NOW() 当前时间:USER 当前用户 3.GRANT A ...
- MVC 返回404,返回图片,流到数组,apk信息
return HttpNotFound(); byte[] buffer0 = QRCode(); return File(buffer0, @"image/jpeg"); // ...