浏览 4891 次
锁定老帖子 主题:自定义ClassLoader遇到的问题
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-09-16
package classloader; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; /** * 加载类----->定义类------>解析类 * loadClass---->defineClass ------>resolveClass */ class MyClassLoader extends ClassLoader { public static void main(String[] args)throws Exception{ ClassLoader loader = new MyClassLoader(); Class clazz = loader.loadClass("classloader.Pig"); Object pig = clazz.newInstance(); System.out.println(pig.toString()); System.out.println(clazz.getClassLoader()); System.out.println(pig); } public MyClassLoader(){ } /* 类加载器 * resolve:参数告诉方法是否需要解析类。在准备执行类之前,应考虑类解析。并不总是需要解析。如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。 */ public Class loadClass( String name, boolean resolve ) throws ClassNotFoundException{ // 目标Class Class clas = null; // 看是否已经加载该类 clas = findLoadedClass( name ); if(clas == null){ clas = findClass(name); } //如果class对象不存在则在系统中查找 if (clas==null) { //它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将原始字节转换成 Class 对象 //核心:使用的ClassLoader是系统默认的:sun.misc.Launcher$AppClassLoader@131f71a clas = findSystemClass( name ); } if(clas == null){ throw new ClassNotFoundException("该类不存在"); } //是否需要解析该类 if (resolve && clas != null) resolveClass( clas ); return clas; } //构造该类的Class对象 public Class findClass(String name){ try{ byte[] b = loadClassBytes(); if(b == null){ return null; } return defineClass("classloader.Pig", b, 0, b.length); }catch(Exception e){ System.out.print(e.getMessage()); } return null; } /** * 加载编译后字节码文件的数据 */ private byte[] loadClassBytes() throws ClassNotFoundException { try { String classFile = "c:/temp/Pig.class"; FileInputStream fis = new FileInputStream(classFile); FileChannel fileC = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel outC = Channels.newChannel(baos); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { int i = fileC.read(buffer); if (i == 0 || i == -1) { break; } buffer.flip(); outC.write(buffer); buffer.clear(); } fis.close(); return baos.toByteArray(); } catch (IOException fnfe) { throw new ClassNotFoundException(className); } } } 2.Pig类的代码如下: package classloader; public class Pig { public static void main(String[] args){ System.out.println("OK"); } } Pig.class文件拷贝到c:/temp目录下。 出现的问题:在自定义的MyClassLoader中return defineClass("classloader.Pig", b, 0, b.length);报异常: Exception in thread "main" java.lang.ClassCircularityError: classloader/Pig at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(Unknown Source) at java.lang.ClassLoader.defineClass(Unknown Source) at classloader.MyClassLoader.findClass(MyClassLoader.java:69) at classloader.MyClassLoader.loadClass(MyClassLoader.java:40) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClassInternal(Unknown Source) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(Unknown Source) at java.lang.ClassLoader.defineClass(Unknown Source) at classloader.MyClassLoader.findClass(MyClassLoader.java:69) at classloader.MyClassLoader.loadClass(MyClassLoader.java:40) at java.lang.ClassLoader.loadClass(Unknown Source) at classloader.MyClassLoader.main(MyClassLoader.java:19) 如果Pig.java不带包名(即去掉:package classloader;)。然后MyClassLoader里相应的包名也都去掉。就完全没问题。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-09-16
已解决,是因为还需要loadClassPig的父类Object一次。
|
|
返回顶楼 | |
发表时间:2008-09-16
引用 已解决,是因为还需要loadClassPig的父类Object一次。
由于你每次加载类都是加载Pig类,所以加载父类就认为循环继承了,所以抛出ClassCircularityError。 关键是为什么会加载2次?也就是说第一次调用完defineClass("Pig")以后,虚拟机会再次使用Pig的loader去加载Pig父类,我找了下Vm spec,有这么一段说明: 引用 5.3 Creation and Loading
Creation of a class or interface C denoted by the name N consists of the construction in the method area of the Java virtual machine (§3.5.4) of an implementation-specific internal representation of C. Class or interface creation is triggered by another class or interface D, which references C through its runtime constant pool. Class or interface creation may also be triggered by D invoking methods in certain Java class libraries (§3.12) such as reflection. 引用 The Java virtual machine uses one of three procedures to create class or interface C denoted by N:
If N denotes a nonarray class or an interface, one of the two following methods is used to load and thereby create C : If D was defined by the bootstrap class loader, then the bootstrap class loader initiates loading of C (§5.3.1). If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C (§5.3.2). 这应该很说明问题了,至于下午你说不含包名的那个为什么不报错,是由于你加载文件并不是固定加载Pig,所以不会让虚拟机以为循环继承. |
|
返回顶楼 | |
发表时间:2008-09-17
多谢大兵瑞恩(leadyu)指点
为什么调用完defineClass("Pig")以后,虚拟机会再次使用Pig的loader去加载Pig父类? 有个C类,被D类引用,那么当加载D类时就有3种情况,其中一种就是说,加载完D以后再加载所有D引用到的类,所以虚拟机又发起一个loadClassInternal.也就是说用加载D的classLoader去加载 包括去加载父类。 但一般来说父类已经被加载了,只不过上面写的loader并没有遵循委托策略,所以又重复define一次 也就是自定义的loader总有一个父loader,loadClass时总是先问父是否已加载,最后才是自己加载,也就是说,自己只负责自己关心得那些目录下的类文件,其他的都一级级委托给其他loader。一般来说是采用父子委托,但中间件有时也采用向下委托,反正要保证loader之间不会有交叉的边界,否则就乱了,所以自己实现loader,一般不改写loadClass方法,只复写findClass,因为这种策略其实已经内置了。 |
|
返回顶楼 | |
发表时间:2008-09-23
hi ttitfly
你好 能不能把你能运行的代码再上传一下. |
|
返回顶楼 | |
发表时间:2008-09-23
把public Class loadClass( String name, boolean resolve ) throws ClassNotFoundException函数注释掉就可以了
|
|
返回顶楼 | |
发表时间:2008-10-17
最近搜索class文件加密的方法,去重写ClassLoader类.也遇到实例化两次的情况. 借宝地一用 我自定义的类加载器,在 protected final Class<?> defineClass(String name,
byte[] b, int off, int len) throws ClassFormatError 定义类的时候,(目标类是 %classpath%/applicationmain.MainGui.class ).
总是抛出异常 Exception in thread "main" java.lang.NoClassDefFoundError: MainGui (wrong name: applicationmain/MainGui) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.lang.ClassLoader.defineClass(ClassLoader.java:465) at myclassloader.MyClassLoader.findClass(MyClassLoader.java:186) at myclassloader.MyClassLoader.loadClass(MyClassLoader.java:145) at myclassloader.MyClassLoader.loadClass(MyClassLoader.java:85) at myclassloader.MyClassLoader.main(MyClassLoader.java:435)
但是如果我在classpath下直接定义一个Test类.即路径是 %classpath%/Test.class 类加载就是成功的.定义类跟包有关系吗?
另外, 或者defineClass("Test",.....
|
|
返回顶楼 | |