前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java类加载器详解(下)

Java类加载器详解(下)

作者头像
Java团长
发布2018-07-23 19:18:46
6040
发布2018-07-23 19:18:46
举报

再来看一下自定义的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文件的时候要注意了。

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-04-05,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 Java团长 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 注意的两个问题:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com