简体   繁体   中英

How to load two versions of a class from two different versions of jars in a java maven project?

I have a maven project which has a dependency on a jar "sample" of version "2.0". The "sample" jar of version "2.0" contains an Enum by the name "SampleEnum". The SampleEnum in "2.0" version looks as below

public enum SampleEnum {
    "HERBIVORES",
    "CARNIVORES",
    "OMNIVORES"
}

I want to load the "SampleEnum" class from "sample" jar of version "1.0" in the above mentioned maven java project. The SampleEnum class in "1.0" version looks something as below.

public enum SampleEnum {
    "HERBIVORES",
    "CARNIVORES"
}

I used the below code to load the class:

public class Test
{
    public static void main(String args[]) {
        try {
            URLClassLoader loader1 = new URLClassLoader(new URL[] {new File("sample-1.0.jar").toURL()}, Thread.currentThread().getContextClassLoader());

            Class<?> c1 = loader1.loadClass("SampleEnum");

            for (Object o : c1.getEnumConstants()) {
                System.out.println(o);
            }
        }
        catch(Exception ex)
        {
            System.err.println(ex.getMessage());
        }
    }
}

The Actual Output of the above program is:

HERBIVORES CARNIVORES OMNIVORES

As I have loaded the class from "sample-1.0" jar I expected the following output:

HERBIVORES CARNIVORES

May I know the reason why it is loading the class from the dependent jar instead of the specified jar?

And is there any way to load the enum class from version 1.0 jar in the maven project?

The reason it is loading the class from the dependent jar is because the loader1 class loader gets the current Thread's context class loader as its parent class loader and that class loader loads classes from the dependent jar. The public ClassLoader.loadClass(String) method calls into the protected ClassLoader.loadClass(String, boolean) method and that method is documented to explain that the parent class loader is always searched first. You can, of course, override this behavior by subclassing URLClassLoader and overriding the loadClass(String, boolean) method.

org.sample.SampleEnum from sample-1.0.jar

public enum SampleEnum
{
    HERBIVORES,
    CARNIVORES
}

org.sample.SampleEnum from sample-2.0.jar

public enum SampleEnum
{
    HERBIVORES,
    CARNIVORES,
    OMNIVORES
}

Main.java

package com.example.app;

import org.sample.SampleEnum;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import static java.util.Arrays.asList;

public class Main
{
    public static void main(String[] args)
    {
        System.out.println("v2.0: " + asList(SampleEnum.values()));
        try {
            URLClassLoader loader1 = new CustomURLClassLoader(
                    new URL[] { new File("../sample-1.0/target/sample-1.0.jar").toURL() });

            Class<?> c1 = loader1.loadClass("org.sample.SampleEnum");

            System.out.println("v1.0: " + asList(c1.getEnumConstants()));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

class CustomURLClassLoader extends URLClassLoader
{
    public CustomURLClassLoader(URL[] urls)
    {
        super(urls);
    }

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();

                // First, look in the current ClassLoader
                long t1 = System.nanoTime();
                try {
                    c = findClass(name);
                } catch (ClassNotFoundException ex) {
                    // ClassNotFoundException thrown if class not found
                }

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();

                // Lastly, look in the parent ClassLoader
                try {
                    if (getParent() != null) {
                        c = super.loadClass(name, resolve);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
}

Output:

$ java -cp sample-app-2.0.jar:sample-2.0.jar com.example.app.Main
v2.0: [HERBIVORES, CARNIVORES, OMNIVORES]
v1.0: [HERBIVORES, CARNIVORES]

For more information, see https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,%20boolean)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM