【问题标题】:How to load two versions of a class from two different versions of jars in a java maven project?java - 如何在java maven项目中从两个不同版本的jar中加载两个版本的类?
【发布时间】:2017-01-25 08:01:06
【问题描述】:

我有一个 Maven 项目,它依赖于版本“2.0”的 jar“样本”。 版本“2.0”的“sample”jar 包含一个名为“SampleEnum”的 Enum。 “2.0”版本中的 SampleEnum 如下所示

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

我想从上述 maven java 项目中的“1.0”版本的“sample”jar 中加载“SampleEnum”类。 “1.0”版本中的 SampleEnum 类如下所示。

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

我使用下面的代码来加载类:

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());
        }
    }
}

上述程序的实际输出为:

草食动物 食肉动物 杂食动物

当我从“sample-1.0”jar 加载类时,我期望得到以下输出:

草食动物 食肉动物

我可以知道它从依赖 jar 而不是指定 jar 加载类的原因吗?

有没有办法从 maven 项目的 1.0 版 jar 中加载枚举类?

【问题讨论】:

    标签: java maven jar


    【解决方案1】:

    它从依赖 jar 加载类的原因是因为 loader1 类加载器将当前线程的上下文类加载器作为其父类加载器,并且该类加载器从依赖 jar 加载类。公共ClassLoader.loadClass(String) 方法调用受保护的ClassLoader.loadClass(String, boolean) 方法,并记录了该方法以解释始终首先搜索父类加载器。当然,您可以通过继承 URLClassLoader 并覆盖 loadClass(String, boolean) 方法来覆盖此行为。

    org.sample.SampleEnum 来自 sample-1.0.jar

    public enum SampleEnum
    {
        HERBIVORES,
        CARNIVORES
    }
    

    org.sample.SampleEnum 来自 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;
            }
        }
    }
    

    输出:

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

    欲了解更多信息,请参阅https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,%20boolean)

    【讨论】:

    • 使用上面的代码我得到以下异常。线程“main”中的异常 java.lang.NoClassDefFoundError: java/lang/Object
    • 引起:java.lang.ClassNotFoundException: java.lang.Object at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at CustomURLClassLoader.loadClass(CustomURLClassLoader.java:22) at java .lang.ClassLoader.loadClass(ClassLoader.java:357)
    • 我的代码中没有看到该错误。我更新了我的示例,以准确显示我正在运行的内容。
    • 在代码中,您已添加,但您在任何地方都没有使用 CustomURLClassLoader。所以我认为它不会给你想要的输出。如果您尝试在代码中使用 CustomURLLoader,您可能会看到我所说的问题。
    • 我为这个错误道歉。我修复了我的代码调用super.loadClass(...)
    猜你喜欢
    • 2013-10-24
    • 1970-01-01
    • 2017-11-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 2017-07-06
    • 2016-08-31
    相关资源
    最近更新 更多