Custom URLClassLoader, NoClassDefFoundError when run

前端 未结 3 1731
忘掉有多难
忘掉有多难 2021-01-21 03:03

I\'ve created my own URLClassLoader, and set it as the system classloader via java.system.class.loader. It\'s initialized and everything, but the clas

3条回答
  •  醉梦人生
    2021-01-21 03:50

    I would have to agree with the comments on this question. Based on the code you have provided, it would appear that you are getting the error due to the JAR files not being where you expect them to be. As mentioned by @Andrew, you are not checking the existence of the file in your addJarToClasspath method. As a result, if the file does not exist, you will receive a ClassNotFound exception as you are seeing. I verified this problem by taking your ClassLoader logic and passing it a valid and an invalid JAR. When a valid JAR/path was provided, the ClassLoader loaded the class as expected. When an invalid JAR/path was specified, I received the error you mentioned. The URLClassLoader does not throw an exception if an URL is specified that does not point to a valid file.

    To validate the scenario, print out the path of the full path of your File and see if it is correct for the execution environment.

    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:

    • Use a script to detect the environment and set the classpath accordingly. This is perhaps the simplest solution, but one you may or may not want to take based on your particular requirements.
    • Similar to what was mentioned in other answers, specifically load and execute your application using your custom ClassLoader. This does not mean creating a single class that will be loaded and then invoke your application. It means that any class that needs to interact with the dynamically loaded swt libraries and any classes that need to reference your application classes should be loaded from your custom ClassLoader. Any application dependencies, such as log4j, etc, can be referenced by the default application ClassLoader. Here is an example of how this would work:

    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): Includes all class which depend on 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 class would be executed by the VM without the rest of your application being included in the execution Jar.

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

    I see that there have been updates to the other answers. Since I already had typed up the above comments, I figured that I should still post it for your perusal.

提交回复
热议问题