`

Java程序员从笨鸟到菜鸟之(九十九)深入java虚拟机(八)开发自己的类加载器

阅读更多

 

欢迎阅读本专题的其他博客:


 

 

在大多数情况下,系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输Java 类的字节代码,为了保证安全性,这些字节代码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在 Java 虚拟机中运行的类来。下面将通过两个具体的实例来说明类加载器的开发。

 

要创建自定义的类加载器只需要扩展java.lang.ClassLoader 类就可以,然后覆盖它的 findClass(Stringname) 方法即可。该方法根据参数指定的类的名字,去找对应的 class 文件。然后返回 class 对应的对象。下面我们就根据我们自定义的类加载器的源码来具体详解一下这个自定义的步骤:


自定义的类加载器:

 

package com.bzu.csh.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
 * 
 * @author 曹胜欢
 * @version 1.0
 *
 */
public class MyClassLoader extends ClassLoader {
private String name; // 类加载器的名字
private String path = "d:\\"; // 加载类的路径
private final String fileType = ".class"; // class文件的扩展名
public MyClassLoader(String name) {
super(); // 让系统类加载器成为该类加载器的父加载器
this.name = name;
}
public MyClassLoader(ClassLoader parent, String name) {
super(parent); // 显式指定该类加载器的父加载器
this.name = name;
}
@Override
public String toString() {
return this.name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/**
 * @param 类文件的名字
 * @return 类文件中类的class对象
 * 
 * 在这里我们并不需要去显示的调用这里的findclass方法,在上篇文章中,我们通过查看
 * loadclass的源码可以发现,她是在loadclass中被调用的,所以这里我们只需重写这个方法,
 * 让它根据我们的想法去查找类文件就ok,他会自动被调用
 * 
 * 
 * defineClass()将一个 byte 数组转换为 Class 类的实例。必须分析 Class,然后才能使用它
 * 参数:
     * name - 所需要的类的二进制名称,如果不知道此名称,则该参数为 null
     * b - 组成类数据的字节。off 与 off+len-1 之间的字节应该具有《Java Virtual Machine Specification》定义的有效类文件的格式。
     * off - 类数据的 b 中的起始偏移量
     * len - 类数据的长度 
 */
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = this.loadClassData(name);//获得类文件的字节数组
return this.defineClass(name, data, 0, data.length);//
}
/**
 * 
 * @param 类文件的名字
 * @return 类文件的 字节数组
 * 通过类文件的名字获得类文件的字节数组,其实主要就是用
 * 输入输出流实现。
 */
private byte[] loadClassData(String name) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
try {
this.name = this.name.replace(".", "\\");
is = new FileInputStream(new File(path + name + fileType));
baos = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = is.read())) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}


 

 

我想上面的注释中已经足够让大家明白这个自定义类加载器的原理了。在这我在重复的从上到下的再说一遍,加深一下大家的理解。首先在构造方法中,我们可以通过构造方法给类加载器起一个名字,也可以显示的指定他的父累加器器,如果没有显示的指出父类加载器的话他默认的就是系统类加载器。由于我们继承了ClassLoader 类,所以它自动继承了父类的 loadclass方法。我们以前看过 loadclass 的源码知道,它调用了 findclass 方法去查找类文件。所以在这里我们重写了 ClassLoader findclass 方法。在这个方法中首先调用 loadClassData方法,通过类文件的名字获 得类文件的字节数组,其实主要就是用输入输出流实现。然后调用defineClass() 将一个字节数组转换为 Class 类的实例。有时候我们手动生成的二进制码的 class 文件被加密了。所以在我们在利用我们自定义的类加载器的时候还要写一个解密的方法进行解密,这里我们就不实现了。


 

我们实现了自定义类加载器,下一步我们来看一下我们怎么来应用我们这个自定义的类加载器:


 

public static void main(String[] args) throws Exception {
//创建一个loader1类加载器,设置他的加载路径为d:\\serverlib\\,设置默认父加载器为系统类加载器
MyClassLoader loader1 = new MyClassLoader("loader1");
loader1.setPath("d:\\myapp\\serverlib\\");
//创建一个loader2类加载器,设置他的加载路径为d:\\clientlib\\,并设置父加载器为loader1
MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
loader2.setPath("d:\\myapp\\clientlib\\");
//创建一个loader3类加载器,设置他的加载路径为d:\\otherlib\\,并设置父加载器为根类加载器
 MyClassLoader loader3 = new MyClassLoader(null, "loader3");
 loader3.setPath("d:\\myapp\\otherlib\\");
 test(loader2);
 System.out.println("----------");
 test(loader3);
}
public static void test(ClassLoader loader) throws Exception {
Class clazz = loader.loadClass("com.bzu.csh.test.Sample");
Object object = clazz.newInstance();
}


 


类加载器结构图


 

(PS:突然发现 WPS 自带的画图工具也挺好用,虽然有点难看,哈哈)


 

当执行这段代码的时候。首先让loader2 去加载 Sample 类文件,当然我们在执行这段代码的前提时在各个默认加载器中已经有我们 Sample class 文件。 Loader2 首先让父加载器是 loader1 去加载,然后 loader1 会让系统类加载器去加载,系统类加载器会让扩展类加载器加载,扩展类加载器会让根类加载器加载,由于系统类加载器,扩展类加载器,根类加载器的默认路径中都没有我们要的 sample 类,所以 loader2 的默认路径有 sample 这个类,也就是说 loader2 会去加载这个 sample 类。当执行 test loader3) 的时候,由于 loader3 的默认父加载器是根类加载器,并且根类加载前默认路径没有对应的 sample.class 文件,所以,直接的 loader3 类加载器就去加载这个类。

最后要说明的一点是,自定义类加载不光只能从我们本地加载到class 文件,我们也可以加载网络, 即基本的场景是:Java 字节代码( .class )文件存放在服务器上,客户端通过网络的方式获取字节代码并执行。当有版本更新的时候,只需要替换掉服务器上保存的文件即可。通过类加载器可以比较简单的实现这种需求。 其实他的实现和本地差不多,基本上就是geclassdata方法改变了一些。 下面我们来具体看一下:

 

private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
URL url = new URL(path);
InputStream ins = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}


 

 

在通过网络加载了某个版本的类之后,一般有两种做法来使用它。第一种做法是使用Java 反射 API 。另外一种做法是使用接口。 需要注意的是,并不能直接在客户端代码中引用从服务器上下载的类,因为我们写的类加载器被加载所用的类加载器和我们加载的网络类不是同一个类加载器,所以客户端代码的类加载器找不到这些类。使用Java 反射 API 可以直接调用 Java 类的方法。而使用接口的做法则是把接口的类放在客户端中,从服务器上加载实现此接口的不同版本的类。在客户端通过相同的接口来使用这些实现类。

 

不同类加载器的命名空间关系:


1.同一个命名空间内的类是相互可见的。

2.子加载器的命名空间包含所有父加载器的命名空间。因此子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。

3.由父加载器加载的类不能看见子加载器加载的类。

4.如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。

5.当两个不同命名空间内的类相互不可见时,可以采用Java 的反射机制来访问实例的属性和方法。

 

 

------------------------------------------------------------------------------------------------------------------------

广告:我参加了2012年度IT博客大赛,希望大家能多多支持

 

 

 

 

 

 

分享到:
评论

相关推荐

    Java程序员由菜鸟到笨鸟学习文档

    Java程序员的成长之路是一个充满挑战与探索的过程,从初学者到熟练掌握各项技能,需要系统性的学习和实践。"Java程序员由菜鸟到笨鸟学习文档"就是这样一个旨在帮助初入Java世界的学习者逐步进阶的资源。它覆盖了从...

    java程序员由菜鸟到笨鸟

    《java程序员由菜鸟到笨鸟》 刚开始学习java时看过的一个学习资料。 其中包括开发环境搭建、面向对象、Javascript、设计模式、SSH、jquery、java虚拟机等内容。 设计内容较广,可以学习参考。

    实时监控体系:基于Prometheus的API性能指标可视化方案.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    5个提升DeepSeekAPI生成质量的调参技巧,开发者必看!.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    ACM动态规划模板-区间修改线段树问题模板

    ACM动态规划模板-区间修改线段树问题模板

    深度解析C语言调试技巧:VSCode+GDB实战排错指南.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

    10个高效调用DeepSeekAPI的技巧:从请求优化到缓存策略.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    基于Python语言的PersonRelationKnowledgeGraph设计源码

    本项目为Python语言开发的PersonRelationKnowledgeGraph设计源码,总计包含49个文件,涵盖19个.pyc字节码文件、12个.py源代码文件、8个.txt文本文件、3个.xml配置文件、3个.png图片文件、2个.md标记文件、1个.iml项目配置文件、1个.cfg配置文件。该源码库旨在构建一个用于表示和查询人物关系的知识图谱系统。

    成本优化指南:通过Token计算模型将API费用降低57%的秘诀.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    大华智能物联平台,的对接其他接口的API,可以获得视频拉流的flv/hls/rstp 的拉流地址,demo项目为springBoot项目,可以通过摄像头的视频通道,获取到实时拉流的uRl

    rtsp实时预览接口URL:/evo-apigw/admin/API/MTS/Video/StartVideo HLS、FLV、RTMP实时预览接口方式 :接口URL/evo-apigw/admin/API/video/stream/realtime 参数名 必选 类型 说明 data true string Json串 +channelId true string 视频通道编码 +streamType true string 码流类型:1=主码流, 2=辅码流,3=辅码流2 +type true string 协议类型:hls,hlss,flv,flvs,ws_flv,wss_flv,rtmp hls:http协议,m3u8格式,端口7086; hlss:https协议,m3u8格式,端口是7096; flv:http协议,flv格式,端口7886; flvs:https协议,flv格式,端口是7896; ws_flv:ws协议,flv格式,端口是7886; wss_flv:wss协议,flv格式,端口是7896; rtmp:rtmp协议,端口是1975;

    Simulink永磁风机飞轮储能系统二次调频技术研究:频率特性分析与参数优化,Simulink永磁风机飞轮储能二次调频技术:系统频率特性详解及参数优化研究参考详实文献及两区域系统应用,simulink

    Simulink永磁风机飞轮储能系统二次调频技术研究:频率特性分析与参数优化,Simulink永磁风机飞轮储能二次调频技术:系统频率特性详解及参数优化研究参考详实文献及两区域系统应用,simulink永磁风机飞轮储能二次调频,系统频率特性如下,可改变调频参数改善频率。 参考文献详细,两区域系统二次调频。 ,核心关键词: 1. Simulink 2. 永磁风机 3. 飞轮储能 4. 二次调频 5. 系统频率特性 6. 调频参数 7. 改善频率 8. 参考文献 9. 两区域系统 以上关键词用分号(;)分隔,结果为:Simulink;永磁风机;飞轮储能;二次调频;系统频率特性;调频参数;改善频率;参考文献;两区域系统。,基于Simulink的永磁风机与飞轮储能系统二次调频研究:频率特性及调频参数优化

    MATLAB驱动的ASR防滑转模型:PID与对照控制算法对比,冰雪路面条件下滑移率与车速轮速对照展示,MATLAB驱动的ASR防滑转模型:PID与对照控制算法对比,冰雪路面条件下滑移率与车速轮速对照图

    MATLAB驱动的ASR防滑转模型:PID与对照控制算法对比,冰雪路面条件下滑移率与车速轮速对照展示,MATLAB驱动的ASR防滑转模型:PID与对照控制算法对比,冰雪路面条件下滑移率与车速轮速对照图展示,MATLAB驱动防滑转模型ASR模型 ASR模型驱动防滑转模型 ?牵引力控制系统模型 选择PID控制算法以及对照控制算法,共两种控制算法,可进行选择。 选择冰路面以及雪路面,共两种路面条件,可进行选择。 控制目标为滑移率0.2,出图显示车速以及轮速对照,出图显示车辆轮胎滑移率。 模型简单,仅供参考。 ,MATLAB; ASR模型; 防滑转模型; 牵引力控制系统模型; PID控制算法; 对照控制算法; 冰路面; 雪路面; 控制目标; 滑移率; 车速; 轮速。,MATLAB驱动的ASR模型:PID与对照算法在冰雪路面的滑移率控制研究

    芯片失效分析方法介绍 -深入解析芯片故障原因及预防措施.pptx

    芯片失效分析方法介绍 -深入解析芯片故障原因及预防措施.pptx

    4131_127989170.html

    4131_127989170.html

    PostgreSQL自动化部署与优化脚本:智能化安装、安全加固与监控集成

    内容概要:本文提供了一个全面的PostgreSQL自动化部署解决方案,涵盖智能环境适应、多平台支持、内存与性能优化以及安全性加强等重要方面。首先介绍了脚本的功能及其调用方法,随后详细阐述了操作系统和依赖软件包的准备过程、配置项的自动生成机制,还包括对实例的安全性和监控功能的强化措施。部署指南给出了具体的命令操作指导,便于新手理解和执行。最后强调了该工具对于不同硬件条件和服务需求的有效应对能力,特别是针对云计算环境下应用的支持特点。 适合人群:对PostgreSQL集群运维有一定基础并渴望提高效率和安全性的数据库管理员及工程师。 使用场景及目标:本脚本能够帮助企业在大规模部署时减少人工介入时间,确保系统的稳定性与高性能,适用于各类需要稳定可靠的数据库解决方案的企业或机构,特别是在大数据量和高并发事务处理场合。 其他说明:文中还提及了一些高级功能如自动备份、流复制等设置步骤,使得该方案不仅可以快速上线而且能满足后续维护和发展阶段的要求。同时提到的技术性能数据也为用户评估其能否满足业务需求提供了直观参考。

    房地产开发合同[示范文本].doc

    房地产开发合同[示范文本].doc

    成本优化实战:DeepSeekAPI的Tokens计算与计费策略拆解.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    安全必读:DeepSeek接口调用中的数据加密与合规实践.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    工程技术承包合同[示范文本].doc

    工程技术承包合同[示范文本].doc

    蓝桥杯开发赛作品源码【基于C语言】

    蓝桥杯开发赛【作品源码】

Global site tag (gtag.js) - Google Analytics