1

更新: この問題は、TestFramework.jar が JUnit に依存しており、(TestFramework) の読み込み時に junit jar が見つからないことが原因である可能性があると思います。私が正しければ、jar がロードされる順序を指定する方法が必要です。

JUnit TestCase の拡張であり、getTestData という名前のメソッドにテスト データを含む Java ソース ファイルをユーザーが指定できるようにするツールを作成しようとしています。次に、このツールは Java ファイルをコンパイルし、結果のクラス ファイルを「classes」ディレクトリに配置し、クラスをロードして getTestData メソッドにアクセスし、テスト データを表す XML ファイルを生成します。

明らかに、ユーザーがソース ファイルを指定できるようにすることで、コンパイル時にそのファイルのすべての依存関係がクラスパスに含まれていることも確認する必要があります。したがって、今のところ、テスト目的で使用しようとしている特定のファイルの依存関係をハードコーディングしました。ユーザーのファイルのコンパイルは現在機能していますが、結果のクラスをロードすると問題が発生します。繰り返しになりますが、クラスをロードするには、クラスが依存するすべてのファイルがクラスパス上にあることを確認する必要があることは明らかです。どういうわけか、クラスのロード中に必要なクラスが見つからず、NoClassDefFoundError が返されます。

以下は、「lib」ディレクトリ内の各 jar ファイルの URL を含む URLClassLoader を作成するために使用している方法です。ローダーはクラス変数であるため、すべてのメソッドで使用できますが、このメソッドでのみ初期化されることに注意してください。

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);
    }

}

コンパイルされたクラス ファイルを実際に検索してロードするコードを次に示します。最初のパラメータは内部を検索するディレクトリです。2 番目のパラメータは、ユーザーが XML ファイルを生成したいすべてのコンパイル済みクラス ファイルのリストです。

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」ディレクトリは、プロジェクト設定を介してランタイム クラスパスに含まれています (GUI の作成を簡素化するために Netbeans を使用しています)。ユーザーが「lib」ディレクトリにjarを動的に追加できるようにしたいので、プロジェクト設定でjarを指定しません。

出力とここで発生している問題については、コードを実行したときにコンソールに表示されるものです。

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();

この時点で、次のような考えられるすべてのことを試したため、途方に暮れています。

  1. junit.jar をランタイム クラスパス (プロジェクト設定) に追加して、このエラーが確実に解消されるようにします。ただし、jar を動的にロードしたいので、これは問題の解決には役立ちません。
  2. ディレクトリ内のすべての jar をロードしようとする代わりに、junit.jar のみを指定します。これでも、上記と同じ失敗が発生します。
  3. プロジェクト構成から「クラス」ディレクトリを削除し、同じコードを使用してそれらのクラスを取り込みます。これはコンパイルされたソースを取り込むために機能しますが、jar の問題は修正されません。
  4. URL で絶対パスではなく相対パスを使用する。これは役に立ちません。
  5. jar のロードを試行する前後に System.out を追加します。結果は、UR​​LClassLoader の作成中に URL のリストを出力した場合と同じです。最初に作成したときとロードを試行した後では何も変わりません。

私が思いつく唯一のことは、使用しようとしている URLClassLoader が使用されていないということですが、なぜそれが起こるのかわかりません。どんな助けでも大歓迎です。御時間ありがとうございます。

4

2 に答える 2

1

問題が見つかりました-TestFramework jarがプロジェクトのクラスパスに含まれていたため、使用されているデフォルトのローダーを使用してロードされます。次に、後でjarを追加すると、TestFramework jarを処理したローダーがそれらを認識しないため、依存関係が欠落していると見なされます。それを修正するために、2 つの個別の jar を作成しました。1 つはインターフェイス クラスのみで、もう 1 つはすべてのクラスで、依存関係と共にそれをロードできるようにしました。

于 2012-04-20T15:56:36.360 に答える
0

クラスローダーを常に使用するようにするのは難しい場合があります。この問題に遭遇したことを覚えており、アプリ全体を起動するために使用されるクラスローダーを作成する必要がありました。

多くのクラスが、ロードしたクラスローダーではなく、標準のクラスローダーを使用したいと考えていたようです。

Java で使用するクラスローダーの設定を試すこともできます。Thread.currentThread().setContextClassLoader()

私が覚えているクラスローダーに関するルールの 1 つは、クラスローダーは、クラス自体をロードしようとする前に、常にその親クラスローダーを最初に試行する必要があるということです。URLClassLoader を使用すると、構築時に親クラスローダーを渡すことができます。現在のクラスローダーを渡してみてください。

于 2012-04-20T15:33:09.387 に答える