JAVA基础知识之JVM-——自定义类加载器
JVM中除了根加载器之外其他加载器都是ClassLoader的子类实例, 可以通过扩展ClassLoader的子类,通过重写方法来实现自定义的类加载器。
ClassLoader中有两个关键的方法如下,
- loadClass(...), 系统调用这个方法来加载指定类的Class对象
在这个方法中,一般需要做四件事,先后顺序如下,
- findLoadedClass(..)看是否已经加载类——加载器的缓存机制
- 在父加载器上调用loadClass(..), 父加载器为null,则使用根加载器加载——加载器的父类委托机制
- 调用findClass方法查找类
- findClass(...),根据名称查找类
ClassLoader中还有一个核心方法 Class defineClass(...),它负责将class文件读入字节数组,并把它转为Class对象。
所以如果需要自定义加载器,只需要重写findClass(...)方法就行了。因为loadClass不仅用到了缓存机制和父类委托机制,还直接调用了findClass类。
下面是一个简单例子来实现自定义加载器, 这个程序通过重写findClass实现自定义类的加载机制,让它在加载之前先编译源文件,这样就实现运行java文件之前先编译它, 从而可以通过这个程序直接执行java源文件。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; public class MyClassLoader extends ClassLoader{
// 读取一个文件的内容
private byte[] getBytes(String filename) throws IOException {
File file = new File(filename);
long len = file.length();
byte[] raw = new byte[(int)len];
try {
FileInputStream fin = new FileInputStream(file);
//一次读取class文件的全部二进制数据
int r = fin.read(raw);
if (r != len) {
throw new IOException("无法读取全部文件: "+r+" != "+raw);
}
return raw;
} catch (IOException e) {
throw e;
}
} //定义编译指定java文件的方法
private boolean compile(String javaFile) throws IOException{
System.out.println("MyClassLoader: " + "正在编译 " + javaFile);
Process p = Runtime.getRuntime().exec("javac " + javaFile);
try {
//其他线程都等这个线程完成
p.waitFor();
} catch (InterruptedException e) {
System.out.println(e);
}
//获取javac线程的退出值
int ret = p.exitValue();
return ret == 0;
} //重写ClassLoader的findClass方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
// 将包路径中的点(.)替换成斜杠(/)
String fileStub = name.replace(".", "/");
String javaFilename = fileStub + ".java";
String classFilename = fileStub + ".class";
File javaFile = new File(javaFilename);
File classFile = new File(classFilename);
//当指定java源文件存在,且class文件不存在,或者java源文件的修改时间比class文件的修改文件更晚时,重新编译
if (javaFile.exists() && (!classFile.exists()
|| javaFile.lastModified() > classFile.lastModified())) {
try {
//如果编译失败,或者该class不存在
if (!compile(javaFilename) || !classFile.exists()) {
throw new ClassNotFoundException("ClassNotFoundException " + javaFilename);
}
} catch (IOException e) {
e.printStackTrace();
}
} //如果class文件存在,将其转换为Class对象
if (classFile.exists()) {
try {
//将class文件的二进制数据读入数组
byte[] raw = getBytes(classFilename);
//调用ClassLoader的defineClass方法将class文件转换成Class对象
clazz = defineClass(name, raw, 0, raw.length);
} catch (IOException e) {
e.printStackTrace();
}
} // 如果clazz为null, 表明失败, 则抛出异常
if (clazz == null) {
throw new ClassNotFoundException(name);
} return clazz;
} public static void main(String[] args) throws Exception {
// 如果运行该程序时没有参数,即没有目标类 if (args.length < 1) { System.out.println("缺少目标类,请安如下格式运行Java源文件");
System.out.println("java MyClassLoader ClassName");
} //第一个参数是要运行的类
String progClass = args[0]; //剩下的参数将做为目标运行时的参数
//将这些参数复制到一个新的数组中
String[] progArgs = new String[args.length - 1];
System.arraycopy(args, 1, progArgs, 0 , progArgs.length);
MyClassLoader mcl = new MyClassLoader();
//加载需要运行的类
Class<?> clazz = mcl.loadClass(progClass);
//获取需要运行类的主方法
Method main = clazz.getMethod("main", (new String[0]).getClass());
Object argsArray[] = {progArgs};
main.invoke(null, argsArray);
}
}
下面写一个测试用的类,
public class Test{
public static void main(String[] args) throws Exception {
System.out.println("this is Test");
}
}
使用 java MyClassLoader Test 执行程序
自定义类加载器的意义
自定义加载类,还能实现以下重要功能,
- 执行代码前自动验证代码签名
- 根据用户提供的密码解密代码, 从而实现代码混淆器来防止代码被反编译*.class文件
- 根据需求动态地加载类
- 根据需求把其他数据以字节码的形式加载到应用中
JAVA基础知识之JVM-——自定义类加载器的更多相关文章
- JVM自定义类加载器加载指定classPath下的所有class及jar
一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...
- JVM 自定义类加载器在复杂类情况下的运行分析
一.自定义类加载器在复杂类情况下的运行分析 1.使用之前创建的类加载器 public class MyTest16 extends ClassLoader{ private String classN ...
- JVM 自定义类加载器
一.创建自定义类加载器 package com.example.jvm.classloader; import java.io.ByteArrayOutputStream; import java.i ...
- (转)JVM——自定义类加载器
背景:为什么要自定义,如何自定义,实现过程 转载:http://blog.csdn.net/SEU_Calvin/article/details/52315125 0. 为什么需要自定义类加载器 网上 ...
- JVM——自定义类加载器
)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理.这个时候你就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着 ...
- java 基础知识学习 JVM虚拟机参数配置
1) 设置-Xms.-Xmx相等: 2) 设置NewSize.MaxNewSize相等: 3) 设置Heap size, PermGen space: Tomcat 的配置示例:修改%TOMCAT_H ...
- Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论
Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...
- Java内存管理-掌握自定义类加载器的实现(七)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoa ...
- JAVA基础知识|java虚拟机(JVM)
一.JVM简介 java语言是跨平台的,兼容各种操作系统.实现跨平台的基石就是虚拟机(JVM),虚拟机不是跨平台的,所以不同的操作系统需要安装不同的jdk版本(jre=jvm+类库:jdk=jre+开 ...
随机推荐
- 创建Java类并实例化的基本过程
package com.sanguosha.java; /* * 面向对象实现的过程 * 1.创建类并设计类的成员(成员变量即属性and成员方法即方法) * 2.通过类来创建类的对象,也称类的实例化 ...
- http://codeforces.com/contest/555/problem/B
比赛时虽然贪了心,不过后面没想到怎么处理和set的排序方法忘了- -,其实是和优先队列的仿函数一样的... 比赛后用set pair过了... #include <bits/stdc++.h&g ...
- spark小技巧-mapPartitions
与map方法类似,map是对rdd中的每一个元素进行操作,而mapPartitions(foreachPartition)则是对rdd中的每个分区的迭代器进行操作.如果在map过程中需要频繁创建额外的 ...
- Microsoft.Jet.OLEDB.4.0和Microsoft.ACE.OLEDB.12.0的区别
Microsoft.Jet.OLEDB.4.0和Microsoft.ACE.OLEDB.12.0的区别 时间 2012-12-19 20:30:12 CSDN博客原文 http://blog.cs ...
- 夺命雷公狗---DEDECMS----28dedecms浏览次数的完成
在页面里显示每部影视作品的浏览量,显示方法如下所示: 首先我们要在内容模板文件里增加显示区: 然后到后保存下文章页的内容页的模版,然后去看下对我们的样式有没有什么影响: 看到这里我们就已经成功一点点噢 ...
- 「linux」win+linux 双系统 默认启动项 的修改
修改/etc/default/grub文件,其中的GRUB_DEFAULT表示默认启动项: sudo gedit /etc/default/grub 注意:启动项是从0开始计数. 要使修改生效需要运行 ...
- 【crunch bang】调整窗口大小
在终端下, <super> + 上箭头 == 向上调整大小 <super> + 下箭头(左.右)
- println()函数输出int类型返回值错误的问题
out.println(); 在用这个语句输出其他类返回大的int类型的数据的时候,注意输出错误. 例如: out.println(class1.方法()): 导致错误: our.println(c ...
- Android_adb详解
adb定义:adb(android debug bridge)是android系统中的一种命令行工具,通过它可以和android设备或模拟器通信. adb工具位置: <path-to-sdk&g ...
- WebService优点和缺点小结(转)
一.什么是WebService? 实际上,WebService的主要目标是跨平台的可互操作性.为了达到这一目标,WebService完全基于XML(可扩展标记语言).XSD (XMLSchema) ...