Search code examples
javajarclassloader

Java - Strange behaviour with dynamic class loader


In my code I want to dynamically load Module class implementations from Jar files.

In my directory I have 3 files: A.jar, B.jar, C.jar Each jar has one class called Main which extends Module class

A.jar code example:

public class Main extends Module {

    private static String name = "A";

    public Main() {
        super(name);
    }

}

(B and C files are the same but with "B" and "C" instead of "A" in the name property).

My Module class code is:

public abstract class Module{

    private StringProperty nameProperty;

    public Module(String name){
        this.nameProperty = new SimpleStringProperty(name);
    }

    public StringProperty nameProperty(){
         return nameProperty;
     }

}

This is the code that I use to dynamically load the three classes:

for (File moduleFile : Data.modulesDir.listFiles()) {

        try {

            URL url = moduleFile.toURI().toURL();
            Class[] parameters = new Class[] { URL.class };
            URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
            Class<URLClassLoader> sysClass = URLClassLoader.class;

            Method method = sysClass.getDeclaredMethod("addURL", parameters);
            method.setAccessible(true);
            method.invoke(sysLoader, new Object[] { url });

            Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("com.ehzlab.webreaper.module.Main")
                    .getConstructor();
            Module instance = (Module) cs.newInstance();

            System.out.println(instance.nameProperty.get());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

I expect this ouput:

A
B
C

but I get this instead:

A
A
A

It seems like that loads the same jar at each file list iteration. But debugging I noted that the URL changes every time. I also tried inverting the order, for example, placing B.jar before the other jar, and the output is:

B
B
B

Why?


Solution

  • Simply because you are using same classloader each time, which doesn't reload underlying classes:

    ClassLoader.getSystemClassLoader()...
    

    In order to get access to specific classes, you have to use appropriate classloader used for loading particular jar file (may be it is sysLoader, not sure, as I didn't check):

    Constructor<?> cs = sysLoader.loadClass("com.ehzlab.webreaper.module.Main")
                        .getConstructor();
    

    Look at this question as well: How should I load Jars dynamically at runtime?