java自定义类加载器的意义_JAVA基础知识之JVM-——自定义类加载器

JVM中除了根加载器之外其他加载器都是ClassLoader的子类实例, 可以通过扩展ClassLoader的子类,通过重写方法来实现自定义的类加载器。

ClassLoader中有两个关键的方法如下,

loadClass(...), 系统调用这个方法来加载指定类的Class对象

在这个方法中,一般需要做四件事,先后顺序如下,

findLoadedClass(..)看是否已经加载类——加载器的缓存机制

在父加载器上调用loadClass(..), 父加载器为null,则使用根加载器加载——加载器的父类委托机制

调用findClass方法查找类

findClass(...),根据名称查找类

ClassLoader中还有一个核心方法 Class defineClass(...),它负责将class文件读入字节数组,并把它转为Class对象。

所以如果需要自定义加载器,只需要重写findClass(...)方法就行了。因为loadClass不仅用到了缓存机制和父类委托机制,还直接调用了findClass类。

下面是一个简单例子来实现自定义加载器, 这个程序通过重写findClass实现自定义类的加载机制,让它在加载之前先编译源文件,这样就实现运行java文件之前先编译它, 从而可以通过这个程序直接执行java源文件。

1 importjava.io.File;2 importjava.io.FileInputStream;3 importjava.io.IOException;4 importjava.lang.reflect.InvocationTargetException;5 importjava.lang.reflect.Method;6

7 public class MyClassLoader extendsClassLoader{8 //读取一个文件的内容

9 private byte[] getBytes(String filename) throwsIOException {10 File file = newFile(filename);11 long len =file.length();12 byte[] raw = new byte[(int)len];13 try{14 FileInputStream fin = newFileInputStream(file);15 //一次读取class文件的全部二进制数据

16 int r =fin.read(raw);17 if (r !=len) {18 throw new IOException("无法读取全部文件: "+r+" != "+raw);19 }20 returnraw;21 } catch(IOException e) {22 throwe;23 }24 }25

26 //定义编译指定java文件的方法

27 private boolean compile(String javaFile) throwsIOException{28 System.out.println("MyClassLoader: " + "正在编译 " +javaFile);29 Process p = Runtime.getRuntime().exec("javac " +javaFile);30 try{31 //其他线程都等这个线程完成

32 p.waitFor();33 } catch(InterruptedException e) {34 System.out.println(e);35 }36 //获取javac线程的退出值

37 int ret =p.exitValue();38 return ret == 0;39 }40

41 //重写ClassLoader的findClass方法

42 protected Class> findClass(String name) throwsClassNotFoundException {43 Class clazz = null;44 //将包路径中的点(.)替换成斜杠(/)

45 String fileStub = name.replace(".", "/");46 String javaFilename = fileStub + ".java";47 String classFilename = fileStub + ".class";48 File javaFile = newFile(javaFilename);49 File classFile = newFile(classFilename);50 //当指定java源文件存在,且class文件不存在,或者java源文件的修改时间比class文件的修改文件更晚时,重新编译

51 if (javaFile.exists() && (!classFile.exists()52 || javaFile.lastModified() >classFile.lastModified())) {53 try{54 //如果编译失败,或者该class不存在

55 if (!compile(javaFilename) || !classFile.exists()) {56 throw new ClassNotFoundException("ClassNotFoundException " +javaFilename);57 }58 } catch(IOException e) {59 e.printStackTrace();60 }61 }62

63 //如果class文件存在,将其转换为Class对象

64 if(classFile.exists()) {65 try{66 //将class文件的二进制数据读入数组

67 byte[] raw =getBytes(classFilename);68 //调用ClassLoader的defineClass方法将class文件转换成Class对象

69 clazz = defineClass(name, raw, 0, raw.length);70 } catch(IOException e) {71 e.printStackTrace();72 }73 }74

75 //如果clazz为null, 表明失败, 则抛出异常

76 if (clazz == null) {77 throw newClassNotFoundException(name);78 }79

80 returnclazz;81 }82

83 public static void main(String[] args) throwsException {84 //如果运行该程序时没有参数,即没有目标类

85

86 if (args.length < 1) {87

88 System.out.println("缺少目标类,请安如下格式运行Java源文件");89 System.out.println("java MyClassLoader ClassName");90 }91

92 //第一个参数是要运行的类

93 String progClass = args[0];94

95 //剩下的参数将做为目标运行时的参数96 //将这些参数复制到一个新的数组中

97 String[] progArgs = new String[args.length - 1];98 System.arraycopy(args, 1, progArgs, 0, progArgs.length);99 MyClassLoader mcl = newMyClassLoader();100 //加载需要运行的类

101 Class> clazz =mcl.loadClass(progClass);102 //获取需要运行类的主方法

103 Method main = clazz.getMethod("main", (new String[0]).getClass());104 Object argsArray[] ={progArgs};105 main.invoke(null, argsArray);106 }107 }

下面写一个测试用的类,

1 public classTest{2 public static void main(String[] args) throwsException {3 System.out.println("this is Test");4 }5 }

使用 java MyClassLoader Test 执行程序

自定义类加载器的意义

自定义加载类,还能实现以下重要功能,

执行代码前自动验证代码签名

根据用户提供的密码解密代码, 从而实现代码混淆器来防止代码被反编译*.class文件

根据需求动态地加载类

根据需求把其他数据以字节码的形式加载到应用中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值