`
ttitfly
  • 浏览: 622081 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

自定义ClassLoader遇到的问题

    博客分类:
  • java
阅读更多
1.自定义的类加载器

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里相应的包名也都去掉。就完全没问题。
分享到:
评论
6 楼 llp20_2000 2008-10-17  
<p>  最近搜索class文件加密的方法,去重写ClassLoader类.也遇到实例化两次的情况.</p>
<p>  借宝地一用<img src='/images/smiles/icon_smile.gif' alt=''/></p>
<p>  我自定义的类加载器,在</p>
<p>       <span style='white-space: pre;'>protected final <a title='java.lang 中的类'>Class</a>&lt;?&gt; <strong>defineClass</strong>(<a title='java.lang 中的类'>String</a> name,</span></p>
<p><span style='font-family: simsun; font-size: 16px;'>
</span></p><pre>                                     byte[] b,
                                     int off,
                                     int len)
                              throws <a title='java.lang 中的类'>ClassFormatError</a></pre>
<pre>定义类的时候,(<span style='white-space: normal;'>目标类是  %classpath%/applicationmain.MainGui.class ).    </span></pre>
<p/>
<p>   总是抛出异常</p>
<p>     <span style='white-space: pre;'>Exception in thread "main" java.lang.NoClassDefFoundError: MainGui (wrong name: applicationmain/MainGui)</span></p>
<pre name='code' class='java'> 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)

</pre>
<p>        </p>
<p>    但是如果我在classpath下直接定义一个Test类.即路径是 %classpath%/Test.class  类加载就是成功的.定义类跟包有关系吗?</p>
<p>    </p>
<p> </p>
<p>   另外, <span style='font-family: simsun; font-size: 16px;'><code><strong><a>defineClass</a></strong>(<a title='java.lang 中的类'>String</a> name, byte[] b, int off, int len)</code> 方法中的第一参数name是要加载类的全名,即 "包名.类名"  吗?而不是类名吗?   如<span style='font-family: -webkit-monospace;'><strong><a>defineClass</a></strong>("<span style='color: #0000ee; text-decoration: underline;'>application.Test"</span>,.....</span></span></p>
<p>                                        或者<span style='font-family: -webkit-monospace; font-size: 16px;'><strong><a>defineClass</a></strong>("<span style='text-decoration: underline; color: #0000ee;'>Test"</span>,.....</span></p>
<p style='margin: 0px;'>                                        </p>
<p>                                                </p>
<p>    </p>
<p>   </p>
<div><br/></div>
<p> </p>
<p> </p>
<p> </p>
5 楼 ttitfly 2008-09-23  
把public Class loadClass( String name, boolean resolve ) throws ClassNotFoundException函数注释掉就可以了
4 楼 zyhwxq 2008-09-23  
hi ttitfly
你好 能不能把你能运行的代码再上传一下.
3 楼 ttitfly 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,因为这种策略其实已经内置了。
2 楼 leadyu 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,所以不会让虚拟机以为循环继承.
1 楼 ttitfly 2008-09-16  
已解决,是因为还需要loadClassPig的父类Object一次。

相关推荐

    使用自定义ClassLoader解决反序列化serialVesionUID不一致问题 _ 回忆飘如雪1

    标题和描述中提到的解决方案是通过自定义`ClassLoader`来处理`serialVersionUID`不一致的问题。以下是几种常见方法的优缺点以及自定义`ClassLoader`的详细解释: 1. **修改序列化byte数据**: 这种方法直接修改已...

    ClassLoader小例子

    - 示例可能还涉及到了对异常处理的演示,因为自定义加载过程中可能会遇到找不到类或非法类文件的问题。 了解和掌握ClassLoader的概念及其应用,对于理解和调试复杂的Java应用程序,尤其是涉及到动态加载和插件化...

    理解Java ClassLoader机制

    同时,当遇到“双亲委派模型”(Parent Delegation Model)引发的问题,如类加载异常时,了解ClassLoader的运作方式可以更快定位和解决问题。 总的来说,Java ClassLoader机制是Java平台的核心特性之一,它使得程序...

    JVM ClassLoader简析

    这些代码可能展示了如何创建自定义ClassLoader,以及如何使用ClassLoader加载非标准位置的类。通过分析这些示例,我们可以更好地理解ClassLoader的工作机制。 总的来说,理解和掌握JVM ClassLoader对于优化Java应用...

    使用classloader动态加载Class

    1. **自定义ClassLoader**:Java允许我们创建自定义的ClassLoader,这通常用于实现动态加载类的需求。自定义ClassLoader需要重写`findClass()`或`loadClass()`方法。`loadClass()`方法是类加载的入口,它会调用`find...

    ClassLoader 深入解析

    如果ClassLoader在加载过程中遇到问题,如找不到类或类定义错误,会抛出ClassNotFoundException或NoClassDefFoundError等异常,需要开发者进行适当的异常处理。 总之,理解ClassLoader的工作原理和机制,可以帮助...

    Tomcat 5.0.18 ClassLoader source code insight

    了解Tomcat的ClassLoader源码,有助于我们在开发过程中遇到类加载问题时进行调试,比如解决类冲突、优化资源加载效率,甚至定制化类加载行为以适应特定的应用场景。 通过阅读和分析`icpc2013.pdf`这个文件,虽然其...

    JAVA 基础培训,JDK和JVM,核心类的介绍和使用

    1. **安全性增强**:在执行不可信代码前,可以使用自定义ClassLoader进行数字签名验证,增加系统的安全性。 2. **定制化构建**:根据用户特定需求动态创建类,实现更为灵活的应用逻辑。 3. **来源多样化**:可以从...

    深入java虚拟机(七)深入源码看java类加载器ClassLoader 1

    《深入Java虚拟机(七)深入源码看...深入学习ClassLoader不仅可以提高编程能力,也有助于解决实际开发中遇到的复杂问题。通过阅读源码,我们可以更深入地了解ClassLoader的工作机制,从而更好地利用Java平台的特性。

    JAVA工程师实习一周总结.docx

    同时,持续反思和总结是成长的关键,每周回顾自己的学习进度和遇到的问题,有助于及时调整学习策略,更好地适应实习岗位。 总的来说,作为Java工程师,熟悉JVM的工作原理和ClassLoader机制是基础,而能够灵活运用...

    深入Java虚拟机-ClassLoader.pptx

    用户还可以自定义类加载器,继承`java.lang.ClassLoader`,实现自己的类加载策略。类加载器可以在预期需要某个类时预加载,但如果预加载失败,只有在类被首次主动使用时才会抛出错误。 连接阶段的验证是保证类正确...

    JAVA文件中调用已编译的.CLASS的类.doc

    3. **自定义ClassLoader** `ClassLoader`是Java中用于加载类的抽象类。Java默认提供了一些ClassLoader,如`Bootstrap ClassLoader`、`Extension ClassLoader`和`Application ClassLoader`。开发者也可以通过继承`...

    java-Independent-diamond.rar_Diamond

    Java独立钻石问题,通常指的是在Java编程中遇到的类加载器问题,特别是在处理类的继承关系时出现的类冲突现象。这个问题源于Java的类加载机制,即类加载器(ClassLoader)如何查找和加载类。在Java中,每个类都有一...

    java 动态编译.java文件,动态运行类 _1.1

    这里需要注意,因为新编译的类不在原始的ClassPath中,所以需要自定义ClassLoader,比如使用URLClassLoader。 ```java URL classUrl = new File("path/to/compiled/classes").toURI().toURL(); // 替换为实际的编译...

    JAVA类加载器分析--热部署的缺陷(有代码示例,及分析)

    这篇博文“JAVA类加载器分析--热部署的缺陷”探讨了Java类加载机制以及在热部署场景下可能遇到的问题。热部署允许开发者在不重启应用的情况下更新代码,提高开发效率,但同时也存在一些挑战。 首先,我们来理解类...

    Java虚拟机第二版(非扫描版)

    总之,理解和掌握Java虚拟机的运行机制对于Java开发人员来说至关重要,它不仅能够帮助我们编写出更高效、更稳定的代码,还能让我们在遇到问题时能迅速找到解决方案,提升整个系统的运行效率。通过阅读《Java虚拟机第...

    是tuscany与spring,axis整合所有的jar包,已解决冲突问题。

    4. **自定义Classloader**:对于特别复杂的依赖情况,可以考虑使用自定义的类加载器来隔离不同的类路径,从而避免类加载冲突。 #### 示例代码 下面是一个简单的示例,展示了如何在Spring应用中引入Tuscany和Axis的...

    Java的类装载器和命名空间

    在学习过程中,可能会遇到一些混淆的问题,比如类装载器的委托模型、类的加载顺序以及如何自定义类装载器以加载非标准路径的类。理解这些概念有助于解决实际开发中遇到的类加载问题,例如,当需要加载外部库或者...

Global site tag (gtag.js) - Google Analytics