在运行时动态添加jar文件时的NoClassDefFoundError [英] NoClassDefFoundError when dynamically adding jar files at runtime

查看:101
本文介绍了在运行时动态添加jar文件时的NoClassDefFoundError的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:我认为这个问题可能是由于TestFramework.jar依赖于JUnit并且在它(TestFramework)加载时以某种方式找不到junit jar的事实。仍然不确定解决方案,如果我是对的,我需要一种方法来指定加载罐子的顺序。

首先这里是一些背景关于我正在做什么:我正在尝试创建一个工具,允许用户指定一个java源文件,它是JUnit TestCase的扩展,并在名为getTestData的方法中包含测试数据。然后,该工具将编译java文件,将生成的类文件放在classes目录中,加载类并访问getTestData方法,生成表示测试数据的XML文件。

First here is some background on what I'm doing: I'm trying to create a tool that will allow users to specify a java source file which is an extension of a JUnit TestCase and contains test data in a method named getTestData. This tool will then compile the java file, place the resulting class file in the "classes" directory, load the class and access the getTestData method, produce XML files representing the test data.

显然,通过允许用户指定源文件,我还必须确保在编译时该文件的所有依赖项都包含在类路径中。所以现在我已经硬编码了我试图用于测试目的的一个特定文件的依赖项。用户文件的编译当前正在工作,但加载结果类会导致一些问题。再次显而易见的是,加载类我需要确保它所依赖的所有文件都在类路径上,我有。不知怎的,在加载类时,它仍然无法找到所需的类并给我一个NoClassDefFoundError。

Obviously by allowing the user to specify a source file I have to also make sure that all dependencies for that file are included on the classpath when I compile. So for now I have hardcoded the dependencies for one specific file I'm trying to use for testing purposes. The compilation of the user's file is currently working, but loading the resulting class is causing some issues. Again it's obvious that to load the class I need to make sure that all files it depends on are on the classpath, which I have. Somehow during the loading of the class it still can't find required classes and gives me a NoClassDefFoundError.

以下是我用来创建包含URLClassLoader的方法lib目录中每个jar文件的URL。请注意,加载器是一个类变量,因此它可用于我的所有方法,但仅在此方法中初始化。

The following is a method I'm using to create a URLClassLoader containing the URL for each of the jar files in the "lib" directory. Note that the loader is a class variable so it is available to all of my methods but is initialized in this one only.

private void loadLibJars()
{
    try {
        File jarDir = new File("lib");
        ArrayList<URL> jarFiles = new ArrayList<URL>();

        for(File file: jarDir.listFiles())
        {
            if(file.isFile() && file.getName().endsWith(".jar"))
            {
                jarFiles.add(file.toURI().toURL());
            }
        }

        for(URL url:jarFiles)
            System.out.println(url);

        URL[] urlArray = new URL[jarFiles.size()];
        for(int i=0; i<jarFiles.size(); i++)
            urlArray[i] = jarFiles.get(i);

        loader = new URLClassLoader(urlArray);
    } 
    catch (MalformedURLException ex) {
        Logger.getLogger(XmlDataGenerator.class.getName()).log(Level.SEVERE, null, ex);
    }

}

这是实际的代码找到已编译的类文件并加载它。第一个参数是要在其中搜索的目录,第二个参数是用户想要为其生成XML文件的所有已编译类文件的列表。

Here is the code that is actually locating the compiled class file and loading it. The first parameter is the directory to search inside of, the second parameter is a list of all the compiled class files that the user wants to generate the XML files for.

private void findAndLoadClasses(File classesDir, ArrayList<String>classFileNames)
{
    for(File classFile: classesDir.listFiles())
    {
        if(classFile.isDirectory())
        {
            System.out.println(classFile+" is a directory, searching inside of it now");
            findAndLoadClasses(classFile,classFileNames);
        }
        else if(classFile.isFile() && classFileNames.contains(classFile.getName()))
        {
            try
            {
                String fullClassName = classFile.getPath();
                fullClassName = fullClassName.substring(fullClassName.indexOf("\\")+1,fullClassName.lastIndexOf("."));
                fullClassName = fullClassName.replaceAll("\\\\", ".");

                System.out.println("Full class name: "+fullClassName);

                IvrTest testCase =  (IvrTest) Class.forName(fullClassName,true,loader).newInstance();

                System.out.println("Test data from "+classFile.getName()+": "+testCase.getTestData(...));
            }
            catch(Exception ex)
            {
                Logger.getLogger(XmlDataGenerator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

东西要注意的是,重要的是并非所有用户选择的源文件的依赖项都是jar,有些是其他源文件,它们也被编译并放在classes目录中。 classes目录通过项目设置包含在运行时类路径中(我使用Netbeans来简化GUI创建)。由于我希望用户能够将jar动态添加到lib目录,所以我没有在项目设置中指定jar。

Something else to note that could be important is that not all the dependencies for the user-selected source file are jars, some are other source files that are also compiled and placed in the "classes" directory. The "classes" directory is included in the runtime classpath via project settings (I'm using Netbeans to simplify the GUI creation). Since I want the user to be able to dynamically add jars to the "lib" directory I do not specify the jars in the project settings.

关于输出和问题我在这里是我在运行代码时在控制台中看到的内容:

Regarding the output and the problem I'm having here is what I see in my console when I run the code:

List of class files to search for: [MyTest.class]
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/client.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/delegate.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/model.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/common.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/framework.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/TestFramework.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/junit.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/log4j-1.2.15.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.beans-3.0.1.RELEASE-A.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.context-3.0.1.RELEASE-A.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.core-3.0.1.RELEASE-A.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.web-3.0.1.RELEASE-A.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/org.springframework.web.servlet-3.0.1.RELEASE-A.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/reports.jar
file:/C:/Documents%20and%20Settings/username/My%20Documents/NetBeansProjects/XmlDataGenerator/lib/servlet.jar
classes\my is a directory, searching inside of it now
classes\my\package is a directory, searching inside of it now
classes\my\package\name is a directory, searching inside of it now
Full class name: my.package.name.MyTest
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: junit/framework/TestCase
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:296)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:247)
        at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:299)
        at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285)
        at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285)
        at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285)
        at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285)
        at xmldatagenerator.main.XmlDataGenerator.findAndLoadClasses(XmlDataGenerator.java:285)
        at xmldatagenerator.main.XmlDataGenerator.generateXmlBtnActionPerformed(XmlDataGenerator.java:242)
        at xmldatagenerator.main.XmlDataGenerator.access$300(XmlDataGenerator.java:33)
        at xmldatagenerator.main.XmlDataGenerator$4.actionPerformed(XmlDataGenerator.java:104)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
        at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
        at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
        at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
        at java.awt.Component.processMouseEvent(Component.java:6267)
        at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
        at java.awt.Component.processEvent(Component.java:6032)
        at java.awt.Container.processEvent(Container.java:2041)
        at java.awt.Component.dispatchEventImpl(Component.java:4630)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4460)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577)
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
        at java.awt.Container.dispatchEventImpl(Container.java:2085)
        at java.awt.Window.dispatchEventImpl(Window.java:2478)
        at java.awt.Component.dispatchEvent(Component.java:4460)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Caused by: java.lang.ClassNotFoundException: junit.framework.TestCase
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        ... 73 more

XmlDataGenerator.java第299行是读取 IvrTest testCase =(IvrTest)Class.forName(fullClassName,true,loader).newInstance();

XmlDataGenerator.java line 299 is the line that reads IvrTest testCase = (IvrTest) Class.forName(fullClassName,true,loader).newInstance();

此时我不知所措,因为我已经尝试了所有我能想到的事情,例如:

At this point I'm at a loss because I have tried everything I can think of such as:


  1. 将junit.jar添加到运行时类路径(在项目设置中)以确保此错误消失。这无法解决问题,因为我想动态加载jar。

  2. 仅指定junit.jar而不是尝试加载目录中的所有jar。这仍然导致与上面相同的失败。

  3. 从项目配置中删除'classes'目录并使用相同的代码引入这些类。这可以引入已编译的源代码,但不能修复jar问题。

  4. 在URL中使用相对路径而不是绝对路径。这没有帮助。

  5. 在尝试加载jar之前和之后添加额外的System.out - 结果与在创建URLClassLoader期间打印URL列表时的结果相同在我第一次创建它和尝试加载之间发生了变化。

  1. Adding the junit.jar to the runtime classpath (in project settings) to make sure this error goes away, which it does. This doesn't help fix the problem though since I want to load jars dynamically.
  2. Specifying only the junit.jar instead of trying to load all jars in the directory. This still results in a failure same as above.
  3. Removing the 'classes' directory from the project config and using the same code to bring in those classes. This works to bring in the compiled sources but does not fix the jar issue.
  4. Using relative paths in the URLs instead of absolute. This does not help.
  5. Adding extra System.out before and after attempting to load the jars - the results are the same as when I print the list of URLs during the creation of the URLClassLoader nothing has changed between when I first created it and after the load is attempted.

我唯一想到的就是不知何故我试图使用的URLClassLoader没有被使用,但我不知道为什么会这样。任何帮助将不胜感激。感谢您的时间。

The only thing I can come up with is that somehow the URLClassLoader I'm trying to use is not being used but I have no idea why that would be happening. Any help would be greatly appreciated. Thanks for your time.

推荐答案

我发现了问题 - 因为TestFramework jar包含在项目类路径中,所以它将被加载无论使用什么默认加载器。然后,当我稍后添加更多jar时,处理TestFramework jar的加载器看不到它们,因此它认为缺少依赖项。为了修复它,我创建了两个独立的jar,一个只有接口类,另一个有所有类,所以我可以将它与它的依赖项一起加载。

I found the problem - since the TestFramework jar was included in the project classpath it would be loaded using whatever default loader is used. Then when I add more jars later the loader that handled the TestFramework jar does not see them so it thinks the dependencies are missing. To fix it I created two separate jars, one with interface classes only and the other with all classes so that I could load it together with its dependencies.

这篇关于在运行时动态添加jar文件时的NoClassDefFoundError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆