使用setContextClassLoader抛出NoClassDefFoundError异常的问题

编写了一个ClassLoader类,把E:\\helloworld.jar(这个jar中包含test.HelloWorld类)加入到classpath中,然后invoke() “test.Hello类”中的main函数,这个main函数依赖helloworld.jar中的方法

//ClassLoader.java
public class ClassLoader {
    public static void main(String[] args) throws Throwable{    
        
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                System.out.println("Hello!");
            }
        });
        ArrayList<URL> classPath = new ArrayList<URL>();
        classPath.add(new File("E:\\helloworld.jar" + "/").toURL());

        URLClassLoader loader = new URLClassLoader(classPath.toArray(new URL[0]));
        Thread.currentThread().setContextClassLoader(loader);
        Class<?> mainClass = Class.forName("test.Hello", true, loader);
        
        Method main = mainClass.getMethod("main", new Class[] { Array
                .newInstance(String.class, 0).getClass() });
        String[] newArgs = Arrays.asList(args).subList(0, args.length)
                .toArray(new String[0]);
        try {
            main.invoke(null, new Object[] { newArgs });
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
}

//Hello.java
package test;
public class Hello {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        test.HelloWorld.print();
    }
}
//HelloWorld.class/helloworld.jar
package test;
public class HelloWorld {
    public static void print() {
        // TODO Auto-generated method stub
        System.out.println("Hello World!");
    }
}


在eclipse中可以运行,因为我将helloworld.jar引入工程。在命令行下运行时就报NoClassDefFoundError异常



 

感谢解答:-)

 

[quote]

Hello和ClassLoader是在什么位置?

e:\java\workspace\jobutil\bin\test.Hello
e:\java\workspace\jobutil\bin\test.ClassLoader

[quote]
URLClassLoader loader = new URLClassLoader(classPath.toArray(new URL[0]));

14. Thread.currentThread().setContextClassLoader(loader);

15. Class<?> mainClass = Class.forName("test.Hello", true, loader);

[/quote]

Class.forName的时候,loader会将加载动作先自下向上委托给其父类加载,而
加载的时候默认路径包含了当前路径,所以loader的父类AppClassLoader能在
当前路径下也就是bin下面找到Hello,所以AppClassLoader加载Hello成功,
而当Hello调用HelloWorld的时候,也就会使用AppClassLoader去加载HelloWorld,在路径中加载失败,所以报异常,你把bin目录下的Hello删掉,
打包放在E:\helloworld.jar中,就能成功运行了。

在我环境上运行没有任何问题。
你检查下你的java-home配置有没有问题?

[quote]谢谢您的解答。test.HelloWorld是打成helloworld.jar放在e盘下面的。问题应该是:运行Hello的main函数时classpath中没有这个jar包。我在几台机器上试都如此,问题不明。 [/quote]

Hello和ClassLoader是在什么位置?

test包下的两个类是否都打进jar包了,jar是从eclipse中导出的还是jar命令打的

从这里看,除了自定义的ClassLoader这个类名不对,其它都应该是没问题的。ClassLoader与java.lang里的重名了

改变一下 .classpath文件下面相关jar的路径。

确定jar包是放到E盘根目录了吧

检查下系统环境变量中的CLASSPATH的值,最好只保留 . 这个路径。

类名首字母大写 ,编译 javac +文件名.java 解释 java +文件名 路径要切换到你的java文件目录 cd .. 上一级 cd .. 上一级 , cd 目录 ,javac 文件名.java ,java 文件名

在代码里试试URLClassLoader的loadClass方法、findResource方法,如果是NoClassDefFound,这两个方法应该也找不到东西出来

改变一下 .classpath文件下面相关jar的路径。
因为你用myeclipse是它自动给你设置了正确的路径,你现在得自己手动设置。
你试试

Class<?> mainClass = Class.forName("test.Hello", true, loader);

用loader加载类test.Hello,因为helloworld.jar不存在该类,怕以委托给
systemclassloader,所以由systemclassloader加载了test.Hello类,
test.Hello再引用helloworld,系统当然用systemclassloader去加载他。
因为loader不是systemclassloader的父classloader(反过来成立),
所认找不到test.HelloWorld.

java -cp helloworld.jar test.ClassLoader
应该可以,因为systemclassloader就负责加载cp中的类。(具体原因,参考classloader委托原理,google找一找,再不行,contract me)

怕以-->所以

java -cp e;\helloworld.jar test.ClassLoader
test.ClassLoader 的路径也要加在cp中
不然去哪找

另外我第一次说的方法,在eclipse里应该没问题了吧

myeclipse直接运行是没有问题,但在命令行运行的时候,如下设置在本机就是正常的。
java -classpath E:\WorkspacesForJPBM\LoaderJAR\bin; test.ClassLoader,即使把bin目录设置成classpath.

:cry: :cry: :cry: :cry: :cry: :cry: :cry: :cry: :cry: :cry: