自定义 URLClassLoader,运行时出现 NoClassDefFoundError

2023-12-10

我已经创建了自己的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);

可能是什么原因造成的?难道不应该“只是工作”吗?


Update:这是重要的代码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.


我必须同意关于这个问题的评论。根据您提供的代码,您收到错误似乎是由于 JAR 文件不在您期望的位置。正如@Andrew 所提到的,您没有在 addJarToClasspath 方法中检查文件是否存在。因此,如果该文件不存在,您将收到您所看到的 ClassNotFound 异常。我通过获取您的 ClassLoader 逻辑并向其传递有效和无效的 JAR 来验证此问题。当提供有效的 JAR/路径时,类加载器将按预期加载该类。当指定了无效的 JAR/路径时,我收到了您提到的错误。如果指定的 URL 不指向有效文件,则 URLClassLoader 不会引发异常。

要验证该场景,请打印出文件的完整路径,并查看它对于执行环境是否正确。

Edit


It appears that even if you override the system ClassLoader, the VM will still use the default sun.misc.Launcher$AppClassLoader to load some classes. In my testing this includes the classes that are referenced from the main application. I'm sure there is a reason for this process, however, I am unable to ascertain it at this time. I have come up with a few solutions for you:
  • 使用脚本检测环境并相应地设置类路径。这可能是最简单的解决方案,但根据您的特定要求,您可能愿意也可能不想采用。
  • 与其他答案中提到的类似,专门使用自定义类加载器加载和执行您的应用程序。这并不意味着创建一个将被加载然后调用您的应用程序的单个类。这意味着任何需要与动态加载的类交互swt需要引用应用程序类的库和任何类都应从自定义类加载器加载。任何应用程序依赖项,例如 log4j 等,都可以由默认应用程序 ClassLoader 引用。以下是其工作原理的示例:

JAR 1(启动器.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);
    }
}

The AppLauncher类将由 VM 执行,而应用程序的其余部分不会包含在执行 Jar 中。

java -Djava.system.class.loader=test.LibraryLoader -cp :launcher.jar mp.AppLauncher

我看到其他答案已经更新了。既然我已经把上面的评论打出来了,我想我还是应该把它贴出来供大家阅读。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义 URLClassLoader,运行时出现 NoClassDefFoundError 的相关文章

随机推荐