繁体   English   中英

classloader:如何加载不同版本的jar

[英]classloader: how to load different version of jar

我必须使用3rd party平台,但是该平台具有jar libjar-1.0.0.jar的较旧版本,无法替换。 该平台使我可以在其之上运行自己的(平面文件)软件包。 我将新版本的libjar-2.0.0.jar放在我的包/ packages / package-name / external-jar下。 当我使用URLClassLoader加载libjar-2.0.0.jar然后打印出所有声明的方法时,我能够看到2.0.0 jar中的方法。 但是,当我调用时,总是得到NoSuchMethodException 当我打印出newobj.class.getProtectionDomain().getCodeSource().getLocation().toString() ,它始终显示libjar-1.0.0.jar 谁能帮助解释我做错了什么以及在运行时强制使用特定jar中的类需要做什么?

这是我的代码的快照

File f = new File(path);

URL[] urls = new URL[1];
urls[0] = f.toURI().toURL();
ClassLoader cl = new URLClassLoader(urls);

Class<?> utilsClass = cl.loadClass("com.myclass");
Constructor<?> cons = utilsClass.getConstructor(First.class, Second.class);
Object utils = cons.newInstance(firstObj, new Second());
if (utilsClass.getProtectionDomain() != null) {
           LOGGER.info(utilsClass.getProtectionDomain().getCodeSource().getLocation().toString());
}
// this print out --- 1.0.0.jar instead of 2.0.0.jar

for (Method m : utilsClass.getDeclaredMethods()) {
     LOGGER.info("methods: " + m.getName());
}
// method shows the "methodILookFor"

Method m = utilsClass.getDeclaredMethod("methodILookFor", Target.class, String[].class, Object.class);
// always throws NoSuchMethodException

m.invoke(utils, target, string, obj);

类加载的工作方式

  • URLClassLoader用于加载应用程序类路径中尚未指定的类。
    • 类的加载是按照委托原则进行的 如果未加载类,则类加载器会将加载类的任务委托给其父类加载器。 如果父类加载器找不到该类,则将其传递给子类加载器以加载该类。
    • 在您的情况下, URLClassLoader将类加载委托给其父级,即Application Class Loader
    • Application Class Loaderlibjar-1.0.0.jar找到该类。 因此, URLClassLoader最终没有从libjar-2.0.0.jar加载类。

定制类加载器

这是扩展URLClassLoader的自定义类加载器的简单示例。 该类加载器在委托给其父类加载器之前,尝试从其URL加载类。 它应该能够加载示例中所需的不同版本的JAR。 您将在此处找到带有单元测试的完整示例。

PS类加载在Java 9中已更改。未在Java 9中进行测试,并且可能无法正常工作。

public class MyClassLoader extends URLClassLoader {

    public MyClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected synchronized Class<?> loadClass(String name,
            boolean resolve) throws ClassNotFoundException {

        // 1. Check if the class has already been loaded
        Class<?> clazz = findLoadedClass(name);

        ClassLoader parentCL = getParent();

        // 2. If the class is not loaded and the class name starts
        // with 'java.' or 'javax.', delegate loading to parent
        if (clazz == null && parentCL != null && (name.startsWith(
                "java.") || name.startsWith(
                "javax."))) {
            clazz = parentCL.loadClass(name);

        }

        // 3. If the class is still null, try to load the class from the URL
        // (since we have already taken care of 'java.' and 'javax.'
        if (clazz == null) {
            try {
                clazz = super.findClass(name);
            } catch (ClassNotFoundException e) {
                //don't do anything
            }
        }

        // 4. If the class is still null, let the parent class loader load it.
        // Previously, we allowed 'java.' and 'javax.' classes to be loaded
        // from parent
        if (clazz == null && parentCL != null) {
            clazz = parentCL.loadClass(name);
        }

        // 5. If the class is still null, throw a class not found exception
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }

        if (resolve) {
            resolveClass(clazz);
        }

        return clazz;
    }
}

您可以尝试使用自定义类加载器来使用其完全限定名称加载类,而不是使用URL类加载器。 使用这种方法,您应该能够绕过向父类加载器的类加载委派,这会导致您的问题。 因此,您的类加载器应该能够从libjar-2.0.0.jar加载类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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