Java类加载器详解(下)

再来看一下自定义的MyClassLoader.java:

  1. package com.loadclass.demo;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. /**
  8. * 自定义的类加载器
  9. * @author Administrator
  10. */
  11. public class MyClassLoader extends ClassLoader{
  12. //需要加载类.class文件的目录
  13. private String classDir;
  14. //无参的构造方法,用于class.newInstance()构造对象使用
  15. public MyClassLoader(){
  16. }
  17. public MyClassLoader(String classDir){
  18. this.classDir = classDir;
  19. }
  20. @SuppressWarnings("deprecation")
  21. @Override
  22. protected Class<?> findClass(String name) throws ClassNotFoundException {
  23. //class文件的路径
  24. String classPathFile = classDir + "/" + name + ".class";
  25. try {
  26. //将class文件进行解密
  27. FileInputStream fis = new FileInputStream(classPathFile);
  28. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  29. encodeAndDecode(fis,bos);
  30. byte[] classByte = bos.toByteArray();
  31. //将字节流变成一个class
  32. return defineClass(classByte,0,classByte.length);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. return super.findClass(name);
  37. }
  38. //测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下
  39. public static void main(String[] args) throws Exception{
  40. //配置运行参数
  41. String srcPath = args[0];//ClassLoaderAttachment.class原路径
  42. String desPath = args[1];//ClassLoaderAttachment.class输出的路径
  43. String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
  44. String desPathFile = desPath + "/" + desFileName;
  45. FileInputStream fis = new FileInputStream(srcPath);
  46. FileOutputStream fos = new FileOutputStream(desPathFile);
  47. //将class进行加密
  48. encodeAndDecode(fis,fos);
  49. fis.close();
  50. fos.close();
  51. }
  52. /**
  53. * 加密和解密算法
  54. * @param is
  55. * @param os
  56. * @throws Exception
  57. */
  58. private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{
  59. int bytes = -1;
  60. while((bytes = is.read())!= -1){
  61. bytes = bytes ^ 0xff;//和0xff进行异或处理
  62. os.write(bytes);
  63. }
  64. }
  65. }

这个类中定义了一个加密和解密的算法,很简单的,就是将字节和oxff异或一下即可,而且这个算法是加密和解密的都可以用,很是神奇呀!

当然我们还要先做一个操作就是,将ClassLoaderAttachment.class加密后的文件存起来,也就是在main方法中执行的,这里我是在项目中新建一个class_temp文件夹用来皴法加密后的class文件:

同时采用的是参数的形式来进行赋值的,所以在运行的MyClassLoader的时候要进行输入参数的配置:右击MyClassLoader->run as -> run configurations

第一个参数是ClassLoaderAttachment.class文件的源路径,第二个参数是加密后存放的目录,运行MyClassLoader之后,刷新class_temp文件夹,出现了ClassLoaderAttachment.class,这个是加密后的class文件。

下面来看一下测试类:

  1. package com.loadclass.demo;
  2. import java.util.Date;
  3. import java.util.List;
  4. /**
  5. * 测试类
  6. * @author Administrator
  7. */
  8. public class ClassLoaderTest {
  9. @SuppressWarnings("rawtypes")
  10. public static void main(String[] args){
  11. //输出ClassLoaderText的类加载器名称
  12. System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());
  13. System.out.println("System类的加载器的名称:"+System.class.getClassLoader());
  14. System.out.println("List类的加载器的名称:"+List.class.getClassLoader());
  15. System.out.println("默认的类加载器:"+ClassLoaderTest.class.getClassLoader().getSystemClassLoader());
  16. ClassLoader cl = ClassLoaderTest.class.getClassLoader();
  17. while(cl != null){
  18. System.out.print(cl.getClass().getName()+"->");
  19. cl = cl.getParent();
  20. }
  21. System.out.println(cl);
  22. try {
  23. Class classDate = new MyClassLoader("class_temp").loadClass("ClassLoaderAttachment");
  24. Date date = (Date) classDate.newInstance();
  25. //输出ClassLoaderAttachment类的加载器名称
  26. System.out.println("ClassLoader:"+date.getClass().getClassLoader().getClass().getName());
  27. System.out.println(date);
  28. } catch (Exception e1) {
  29. e1.printStackTrace();
  30. }
  31. }
  32. }

运行ClassLoaderTest类,运行结果如下:

ClassLoaderAttachment类的加载器是我们自己定义的类加载器MyClassLoader,同时也输出了Hello ClassLoader字段

到此不要以为结束了,这里还有很多的问题呀,看一下问题的结果是没有问题,但是这里面还有很多的东西需要去理解的,首先来看一下,按照我们之前说的类加载机制,应该是先交给父级的类加载器,AppClassLoader->ExtClassLoader->BootStrap,ExtClassLoader和BootStrap没有找到ClassLoaderAttach.class,但是AppClassLoader类加载器应该能找到呀,可以为什么也没有找到呢?这时因为loadClass方法在使用系统类加载器的时候需要传递全称(包括包名),我们传递ClassLoaderAttachment的话,AppClassLoader也是没有找到这个ClassLoaderAttachment,所以还是MyClassLoader处理了,不信的话可以试一下:

现在将

Class classDate = new MyClassLoader("class_temp").loadClass("ClassLoaderAttachment");

改成:

Class classDate = new MyClassLoader("class_temp").loadClass("com.loadclass.demo.ClassLoaderAttachment");

结果运行:

这时候的加载器是AppClassLoader了,所以要注意loadClass方法传递的参数

到这里我们貌似还没有测试到我们加密后的class文件,我们现在将工程目录中的ClassLoaderAttachment.class删除,将class_temp中加密的ClassLoaderAttachment.class拷贝过去,然后再运行:

这时候就会报错了,不合适的魔数错误(class文件的前六个字节是个魔数用来标识class文件的),这时候就对了,因为ClassLoaderAttachment.class使我们加密后的class文件,AppClassLoader是不认识的,所以报这个错误了,只有用我们自己定义的类加载器来进行解密才可以正确的访问的。到这里总算是说完了,搞了一上午,头都写大了,很是麻烦呀!

注意的两个问题:

1.可能在测试的过程中有这样的情况就是ClassLoaderTest类并没有执行,这个是因为在第一个测试的时候,将ClassLoaderTest类打成.jar放到jre目录中了,所以你后续修改ClassLoaderTest类的话,运行没有效果,因为它加载的类还是jre中的jar中的ClassLoaderTest类,所以你应该将jre中的jar删除即可。

2.就是ClassLoaderAttachment只要保存就会编译成.class文件,所以你在拷贝ClassLoaderAttachment.class文件的时候要注意了。

原文发布于微信公众号 - Java团长(javatuanzhang)

原文发表时间:2017-04-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我是业余自学C/C++的

链队 原

队列用链表来表示时,需要用两个变量来记录队列两端的变化:theFront,theBack.

8320
来自专栏Spark生态圈

[Spark SQL] 源码解析之Analyzer

Analyzer模块将Unresolved LogicalPlan结合元数据catalog进行绑定,最终转化为Resolved LogicalPlan。跟着代码...

13520
来自专栏Java帮帮-微信公众号-技术文章全总结

Java类加载器(用户自定义类加载器实现)

java类加载器主要分为如下几种: jvm提供的类加载器 根类加载器:底层实现,主要加载java核心类库(如:java.lang.*) 扩展类加载器:使用jav...

36560
来自专栏蘑菇先生的技术笔记

qt5中信号和槽的新语法

31470
来自专栏一直在跳坑然后爬坑

RxJava2操作符之“Skip”

Skip操作符可以说是上一篇Take操作符的兄弟 suppress the first n items emitted by an Observable 去...

10730
来自专栏数据结构与算法

11:潜伏者

个人感觉这道题在字符串里面算是一个比较综合比较难的题目了 对于选手的数据查找能力有很强的要求 我在做这道题时运用了桶排的思想 不多说了,看代码吧,全网最简 11...

30160
来自专栏mini188

java中的锁

java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够。于是再次翻看了一下书里的内容,突然有点打开脑门的感觉...

50490
来自专栏何俊林

原来Android还可以这样通过反射,获取jar包属性及方法

1、写一个java类,生成Jar包 2、初始Jar的转换 3、创建一个android工程 4、复制Jar包 5、反射获取属性和方法 5、完整Demo 6、补充 ...

59150
来自专栏Android 研究

APK安装流程详解5——Installer、InstallerConnection和Installd守护进程

因为Installer继承自SystemService,所以我们看下Installer的onStart方法 代码在Installer.java 396行

16610
来自专栏三好码农的三亩自留地

Java动态代理-实战

说动态代理之前,要先搞明白什么是代理,代理的字面意思已经很容易理解了,我们这里撇开其他的解释,我们只谈设计模式中的代理模式

31120

扫码关注云+社区

领取腾讯云代金券