java类加载器学习2——自定义类加载器和父类委托机制带来的问题

一、自定义类加载器的一般步骤

Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制。一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载器。如果父类加载器加载不了,依次再使用其子类进行加载。当然这类所说的父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系。

Java之所以出现这条机制,因为是处于安全性考虑。害怕用户自己定义class文件然后自己写一个类加载器来加载原本应该是JVM自己加载的类。这样会是JVM虚拟机混乱或者说会影响到用户的安全。下面我们来自己实现一个类加载器,其中主要就是继承ClassLoader类。我们有必要明白:

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

①ClassLoader加载类的顺序

1调用findLoadedClass(String)
来检查是否已经加载类

2在父类加载器上调用loadClass方法。如果父亲不能加载,一次一级一级传给子类

3调用子类findClass(String)
方法查找类。若还加载不了就返回ClassNotFoundException,不交给发起请求的加载器的子加载器

②实现自己的类加载器

1
获取类的class文件的字节数组,如loadClassData方法

2
将字节数组转换为Class类的实例,重写findClass中调用的defineClass方法

  1. package cn.M_ClassLoader2;
  2. import java.io.ByteArrayOutputStream;

  3. import java.io.File;

  4. import java.io.FileInputStream;
  5. public class ClassLoaderTest

  6. {

  7. public static void main(String[] args) throws InstantiationException, IllegalAccessException,  ClassNotFoundException

  8. {

  9. // 新建一个类加载器

  10. MyClassLoader cl = new MyClassLoader("myClassLoader");
  11. // 加载类,得到Class对象

  12. Class<?> clazz = cl.loadClass("cn.M_ClassLoader2.Animal");
  13. // 得到类的实例

  14. Animal animal = (Animal) clazz.newInstance();

  15. animal.say();

  16. }

  17. }
  18. class Animal

  19. {

  20. public void say()

  21. {

  22. System.out.println("hello world!");

  23. }

  24. }
  25. class MyClassLoader extends ClassLoader

  26. {

  27. // 类加载器的名称

  28. private String name;
  29. // 类存放的路径

  30. private String path = MyClassLoader.getSystemClassLoader().getResource("").getPath();;
  31. MyClassLoader(String name)

  32. {

  33. this.name = name;

  34. }
  35. MyClassLoader(ClassLoader parent, String name)

  36. {

  37. super(parent);

  38. this.name = name;

  39. }
  40. /**

  41. * 重写findClass方法

  42. */

  43. @Override

  44. public Class<?> findClass(String name)

  45. {

  46. byte[] data = loadClassData(name);

  47. return this.defineClass(name, data, 0, data.length);

  48. }
  49. public byte[] loadClassData(String name)

  50. {

  51. try

  52. {

  53. name = name.replace(".", "//");

  54. FileInputStream is = new FileInputStream(new File(path + name + ".myclass"));

  55. ByteArrayOutputStream baos = new ByteArrayOutputStream();

  56. int b = 0;

  57. while ((b = is.read()) != -1)

  58. {

  59. baos.write(b);

  60. }

  61. System.out.println("我是自定义类加载器哦!");

  62. return baos.toByteArray();

  63. }

  64. catch (Exception e)

  65. {

  66. e.printStackTrace();

  67. }

  68. return null;

  69. }

  70. }

一般来说自己开发的类加载器只需要覆写findClass(String
name)
方法即可。java.lang.ClassLoader类的方法loadClass()封装了前面提到的代理模式的实现。该方法会首先调用findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写loadClass()方法,而是覆写findClass()方法。

二、自定义类加载器的运行问题

由于只重写了findClass方法并没有重写loadClass方法,故没有改变父类委托机制。也就数说如果某个.class可以被父类加载,我们自定义的类加载器就不会被执行了。比如Animal.java被自动编译为Animal.class放在bin目录下,AppClassLoader完全可以加载,所以就不调用自定义的加载器了。

尝试办法1:把Animal.class放在别的目录中比如D盘的根目录下

报 Class A can not access a member of
class B with modifiers
""错。Java语言中的包访问成员实际上指的是运行时包访问可见,而不是编译时。因此当你试图访问不在同一个runtime
package的成员时,即便在编译时它们在同一个包内,但是却由不同的class
loader加载,也同样会得到java.lang.IllegalAccessException: Class A can not access a member
of class B with modifiers "" 这样的异常。

尝试办法2:把该Animal.class的后缀名为.myClass,让AppClassLoader找不到

网上有人说可以解决,但是我实验的结果是会和办法1报一样的异常。

尝试办法3:解决方案是通过扩展自定义的ClassLoader,重写loadClass方法先从当前类加载器加载再从父类加载器加载。

该解决办法是可以解决的,网址是http://blog.csdn.net/zhangxinrun/article/details/6161426

java类加载器学习2——自定义类加载器和父类委托机制带来的问题,码迷,mamicode.com

时间: 2024-10-12 04:07:23

java类加载器学习2——自定义类加载器和父类委托机制带来的问题的相关文章

【正文】Java类加载器( CLassLoader ) 死磕 4: 神秘的双亲委托机制

[正文]Java类加载器(  CLassLoader ) 死磕4:  神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. 类的加载次序 4.4 双亲委托机制原理与沙箱机制 4.5. forName方法和loadClass方法的关系 4.6. 使用组合而不用继承 4.7. 各种不同的类加载途径 4.1.每个类加载器都有一个parent父加载器 每个类加载器都有一个parent父加载器,比如加载SystemConfig.cl

java类加载器和父类委托机制

1.类加载器 Java虚拟机中可以安装多个类加载器,系统默认主要三个类加载器,每个类负责加载特定位置的类:BootStrap(内嵌在java虚拟机中由C++编写),ExtClassLoader,AppClassLoader.当然也可以自定义类加载器,自定义的加载器必须继承ClassLoader. Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载. 2.下面我们来看如下一段代码: p

JAVA类加载器一 父类委托机制

类加载器负责将.class文件加载到内存中,并为之生成对应的Class对象.其中class文件有可能存在磁盘上,也有可能存在网络上. 当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构: 类加载机制:全盘负责和父类委托机制 全盘负责:就是当一个classloader加载一个Class的时候,这个class所依赖的和医用的其他class对象通常也由这个classloader负责载入. 委托机制:先让父类加载器寻找,只有在父类加载器找不到的情况下才从自己的类路径中去寻找. 类加载还采用了

(转)《深入理解java虚拟机》学习笔记6——类加载机制

Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需要完成以下3件事: a.通过一个类的全限定名来获取定义此类的二进制字节流. b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构. c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口. Java虚拟机的类加载是通过类加载器实现的, Java中

Struts2重新学习之自定义拦截器(判断用户是否是登录状态)

拦截器 一:1:概念:Interceptor拦截器类似于我们学习过的过滤器,是可以再action执行前后执行的代码.是web开发时,常用的技术.比如,权限控制,日志记录. 2:多个拦截器Interceptor连在一起组成了Interceptor栈.拦截器是AOP面向切面编程的一种实现,具有热插拔的效应. 3:Struts2拦截器,每个拦截器类只有一个对象实例,即采用了单利模式.所有引用这个拦截器的action都共享着一拦截器类的实例. 拦截器和过滤器的区别 1:拦截器和过滤器的概念非常类似 2:

CXF拦截器介绍及自定义拦截器实现

CXF拦截器是功能的主要实现单元,也是主要的扩展点,可以在不对核心模块进行修改的情况下,动态添加功能.当服务被调用时,会经过多个拦截器链(Interceptor Chain)处理,拦截器链在服务输入(IN)或输出(OUT)阶段实现附加功能,拦截器可以在客户端加入,也可以在服务端加入. 拦截器链的阶段: 拦截器链有多个阶段,每个阶段都有多个拦截器.拦截器在拦截器链的哪个阶段起作用,可以在拦截器的构造函数中声明. 输入拦截器链有如下几个阶段,这些阶段按照在拦截器链中的先后顺序排列. 阶段名称 阶段功

JVM类加载器原理与自定义类加载器

类加载器原理 JVM将class文件字节码文件加载到内存中, 并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class 对象,作为方法区类数据的访问入口. 类缓存 标准的Java SE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间.不过,JVM垃圾收集器可以回收这些Class过象. 类加载器数状结构 引导类加载器(bootstrap class loader) 它用来加载Java的核心库(JAVA_HOME/

java之jvm学习笔记四(安全管理器)

前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器是一个单独的对象,在java虚拟机中,它在访问控制-对于外部资源的访问控制-起到中枢作用 如果光看概念可能并不能很好的理解,或者说比较抽象,下面是ClassLoader其中的一个构造函数,先简单的看看它在初始化ClassLoader之前会做一些什么操作 [java] view plaincopy protected ClassLoader(

struts2学习笔记---自定义拦截器

什么是拦截器? struts2中拦截器分为Struts2定义好的拦截器和自定义的拦截器.其作用是在一个Action执行之前进行拦截,在Action执行之后又加入某些操作. 实现原理 当请求一个Action时,struts2会查找配置文件,并根据这个Action的配置实例化对应的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器. 拦截器的执行流程 1.对Action进行预处理.(正序执行) 2.拦截器自身决定该不该执行后续的拦截器(由invoke()方法的返回值决定).