3

独自URLClassLoaderに作成し、を介してシステムクラスローダーとして設定しましたjava.system.class.loader。初期化されてすべてですが、ロードしようとしているクラスが見つかりません。これがURLClassLoader

public class LibraryLoader extends URLClassLoader
{
    public LibraryLoader(ClassLoader classLoader)
    {
        super(new URL[0], classLoader);
    }
    synchronized public void addJarToClasspath(String jarName) throws MalformedURLException, ClassNotFoundException
    {
        File filePath = new File(jarName);
        URI uriPath = filePath.toURI();
        URL urlPath = uriPath.toURL();

        System.out.println(filePath.exists());
        System.out.println(urlPath.getFile());

        addURL(urlPath);
    }
}

jarが存在し、パスが正しいことを確認しました。これは私が私のプログラムでそれを呼ぶ方法です:

LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
loader.addJarToClasspath("swt.jar");

これは私が得る例外です(166行目は私が新しいものを作成しようとしている行を指しますPoint

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/graphics/Point
        at mp.MyProgram.loadArchitectureLibraries(MyProgram.java:116)
        at mp.MyProgram.main(MyProgram.java:90)
Caused by: java.lang.ClassNotFoundException: org.eclipse.swt.graphics.Point
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 2 more

私は次のようにクラスを明示的にロードしようとしました:

Class.forName("org.eclipse.swt.graphics.Point", false, loader);

これを引き起こしているのは何ですか?それは「うまくいく」べきではありませんか?


更新:これがからの重要なコードですMyProgram

public class MyProgram
{
    // ...

    public static void main(String[] args)
    {
        loadArchitectureLibraries();

        // ...
    }

    public static void loadArchitectureLibraries()
    {
        LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();

        String architecture = System.getProperty("os.arch");
        try {
            if (architecture.contains("64")) {
                loader.addJarToClasspath("swt-3.6.1-win32-win32-x86_64.jar");
            } else {
                loader.addJarToClasspath("swt-3.6.1-win32-win32-x86.jar");
            }

            Class.forName("org.eclipse.swt.graphics.Point", false, loader);
            org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);

        } catch (Exception exception) {

            exception.printStackTrace();
            System.out.println("Could not load SWT library");
            System.exit(1);
        }
    }
}

アップデート2: SSCCEは次のとおりです:http://nucleussystems.com/files/myprogram.zip。を呼び出しjava -Djava.system.class.loader=mp.LibraryLoader -jar myprogram.jarます。

4

3 に答える 3

2

私はこの質問に対するコメントに同意する必要があります。提供したコードに基づいて、JARファイルが期待した場所にないためにエラーが発生しているように見えます。@Andrewが述べたように、addJarToClasspathメソッドにファイルが存在するかどうかはチェックしていません。その結果、ファイルが存在しない場合は、表示されているとおりにClassNotFound例外が発生します。ClassLoaderロジックを取得し、有効なJARと無効なJARを渡すことで、この問題を確認しました。有効なJAR/パスが提供されると、ClassLoaderは期待どおりにクラスをロードしました。無効なJAR/パスが指定された場合、あなたが言及したエラーを受け取りました。有効なファイルを指していないURLが指定されている場合、URLClassLoaderは例外をスローしません。

シナリオを検証するには、ファイルのフルパスのパスを印刷して、実行環境に適しているかどうかを確認します。

編集


システムのClassLoaderをオーバーライドしても、VMはデフォルトを使用してsun.misc.Launcher$AppClassLoader一部のクラスをロードするようです。私のテストでは、これにはメインアプリケーションから参照されるクラスが含まれます。このプロセスには理由があると確信していますが、現時点では確認できません。私はあなたのためにいくつかの解決策を考え出しました:

  • スクリプトを使用して環境を検出し、それに応じてクラスパスを設定します。これはおそらく最も単純な解決策ですが、特定の要件に基づいて採用する場合としない場合があります。
  • 他の回答で述べたのと同様に、具体的には、カスタムClassLoaderを使用してアプリケーションをロードして実行します。これは、ロードされてからアプリケーションを呼び出す単一のクラスを作成することを意味するものではありません。つまり、動的にロードされるswtライブラリと対話する必要のあるクラスと、アプリケーションクラスを参照する必要のあるクラスは、カスタムClassLoaderからロードする必要があります。log4jなどのアプリケーションの依存関係は、デフォルトのアプリケーションClassLoaderによって参照できます。これがどのように機能するかの例を次に示します。

JAR 1(launcher.jar):

public class AppLauncher {
    public static void main(String… args) throws Exception {
        ClassLoader loader = initClassLoader();
        Class<?> mpClass = loader.loadClass("mp.MyProgram");

        // using Runnable as an example of how it can be done
        Runnable mpClass = (Runnable) mpClass.newInstance();
    }
    public static ClassLoader initClassLoader() {
        // assuming still configured as system classloader, could also be initialized directly
        LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();

        // add the main application jar to the classpath.  
        // You may want to dynamically determine this value (lib folder) or pass it in as a parameter
        loader.addJarToClasspath("myapp.jar");

        String architecture = System.getProperty("os.arch");
        try {
            if (architecture.contains("64")) {
                loader.addJarToClasspath("swt-3.6.1-win32-win32-x86_64.jar");
            } else {
                loader.addJarToClasspath("swt-3.6.1-win32-win32-x86.jar");
            }

            Class.forName("org.eclipse.swt.graphics.Point", false, loader);
            org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);

        } catch (Exception exception) {

            exception.printStackTrace();
            System.out.println("Could not load SWT library");
            System.exit(1);
        }
        return loader;
    }

JAR 2(myapp.jar): に依存するすべてのクラスが含まれますswt

public class MyProgram implements Runnable {
    //…
    public void run() {
    // perform application execution

           // this logic should now work
           org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0,0);
    }
}

クラスはAppLauncherVMによって実行され、アプリケーションの残りの部分は実行Jarに含まれません。

java -Djava.system.class.loader = test.LibraryLoader -cp <依存関係jar>:launcher.jar mp.AppLauncher

他の回答が更新されているようです。私はすでに上記のコメントを入力したので、私はあなたの熟読のためにそれをまだ投稿するべきだと思いました。

于 2011-04-27T19:35:50.073 に答える
1

問題のある行はClass.forNameではなく、Pointのインスタンスの実際の初期化であるため、PointクラスをロードしようとするクラスがLibraryクラスローダーによって作成されたことを確認する必要があります。したがって、このブログエントリに従って、LibraryLoaderにいくつかの小さな変更を加えました。

public class LibraryLoader extends URLClassLoader {

    public LibraryLoader(ClassLoader classLoader) {
        super(new URL[0], classLoader);
    }

    synchronized public void addJarToClasspath(String jarName)
            throws MalformedURLException, ClassNotFoundException {
        File filePath = new File(jarName);
        URI uriPath = filePath.toURI();
        URL urlPath = uriPath.toURL();

        System.out.println(filePath.exists());
        System.out.println(urlPath.getFile());

        addURL(urlPath);
    }

    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if ("mp.MyProgram".equals(name)) {
            return getClass(name);
        }
        return super.loadClass(name, resolve);
    }

    private Class<?> getClass(String name) throws ClassNotFoundException {
        String file = name.replace('.', File.separatorChar) + ".class";
        byte[] b = null;
        try {
            b = loadClassData(file);
            Class<?> c = defineClass(name, b, 0, b.length);
            resolveClass(c);
            return c;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private byte[] loadClassData(String name) throws IOException {
        InputStream stream = getClass().getClassLoader().getResourceAsStream(
                name);
        int size = stream.available();
        byte buff[] = new byte[size];
        DataInputStream in = new DataInputStream(stream);
        in.readFully(buff);
        in.close();
        return buff;
    }
}

プログラム自体では、メソッド内から使用されるすべてのクラスが事前にロードされているように見えるため、新しいメソッドを抽出する必要があります。

public class MyProgram {
    public static void main(String[] args) {
        LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();

        String architecture = System.getProperty("os.arch");
        try {
            loader.addJarToClasspath("swt.jar");
            otherMethod();

        } catch (Throwable exception) {
            // println instead of logger because logging is useless at this level
            exception.printStackTrace();
            System.out.println("Could not load SWT library");
            System.exit(1);
        }
    }

    protected static void otherMethod() {
        org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
        System.out.println("Works!");
    }
}

それはあなたのために働くはずです。

于 2011-04-27T20:17:38.740 に答える
1

横にあるカスタムクラスローダーを使用していない(数マイル)離れた場所から見ることができますClass.forName

ClassNoDefFoundErrorは、現在のクラスをロードしたクラスローダーがMyProgramorg.eclipse.swt.graphics.Pointをロードしようとしたために発生します。

Class.forNameを介して別のクラス(ランチャーと呼びます)をロードし、そこから開始する必要があります-何らかのインターフェイスを実装し(実行可能でも可能です)、それを呼び出します。


編集

それを行う方法、単純なシナリオ。1.そのようなRunnableを実装する
という別のクラスを作成します。mp.loader.Launcher

public class Launcher implements Runnable{
public void run(){
  org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
  //whatever, start from here.
}
}

2.swt-loader.jarという別のjarファイルに配置します。

MyProgramクラスでの使用:

loader.addJarToClasspath("swt-loader.jar");
Runnable r = (Runnable) Class.forName("mp.loader.Launcher", true, loader);
r.run();//there you have
于 2011-04-27T21:14:37.013 に答える