简体   繁体   English

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

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

I have to use a 3rd party platform, but the platform has an older version of the jar libjar-1.0.0.jar that cannot be replaced. 我必须使用3rd party平台,但是该平台具有jar libjar-1.0.0.jar的较旧版本,无法替换。 The platform let me run my own (flat file) packages on top of it. 该平台使我可以在其之上运行自己的(平面文件)软件包。 I put the new version of libjar-2.0.0.jar under my package /packages/package-name/external-jar. 我将新版本的libjar-2.0.0.jar放在我的包/ packages / package-name / external-jar下。 When I used URLClassLoader to load libjar-2.0.0.jar and then printing out all declaredmethods, I was able to see the method that is in 2.0.0 jar. 当我使用URLClassLoader加载libjar-2.0.0.jar然后打印出所有声明的方法时,我能够看到2.0.0 jar中的方法。 However, when I invoke, I always get NoSuchMethodException . 但是,当我调用时,总是得到NoSuchMethodException When I print out newobj.class.getProtectionDomain().getCodeSource().getLocation().toString() , it always shows libjar-1.0.0.jar . 当我打印出newobj.class.getProtectionDomain().getCodeSource().getLocation().toString() ,它始终显示libjar-1.0.0.jar Could anyone help explaining what I did wrong and what I need to do to force using the classes in a particular jar during runtime? 谁能帮助解释我做错了什么以及在运行时强制使用特定jar中的类需要做什么?

Here is a snapshot of my code 这是我的代码的快照

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

How Class Loading Works 类加载的工作方式

  • URLClassLoader is used for loading classes that are not already specified in the application classpath. URLClassLoader用于加载应用程序类路径中尚未指定的类。
    • Class loading works on delegation principle . 类的加载是按照委托原则进行的 If a class is not loaded, the task of loading the class is delegated by the class loader to its parent class loader. 如果未加载类,则类加载器会将加载类的任务委托给其父类加载器。 If the class is not found by the parent class loader, it is passed to the child class loader for loading the class. 如果父类加载器找不到该类,则将其传递给子类加载器以加载该类。
    • In your case, the URLClassLoader delegates the class loading to its parent, ie, the Application Class Loader . 在您的情况下, URLClassLoader将类加载委托给其父级,即Application Class Loader
    • The Application Class Loader finds the class in libjar-1.0.0.jar . Application Class Loaderlibjar-1.0.0.jar找到该类。 So, the URLClassLoader ends up not loading the class from libjar-2.0.0.jar . 因此, URLClassLoader最终没有从libjar-2.0.0.jar加载类。

Custom Class Loader 定制类加载器

Here is a simple example of a custom class loader which extends URLClassLoader . 这是扩展URLClassLoader的自定义类加载器的简单示例。 This class loader tries to load classes from its URLs before it delegates to its parent class loader. 该类加载器在委托给其父类加载器之前,尝试从其URL加载类。 It should be able to load different versions of JARs that you need in your example. 它应该能够加载示例中所需的不同版本的JAR。 You will find a complete example here with a unit test. 您将在此处找到带有单元测试的完整示例。

PS Class loading has changed in Java 9. It is not tested with Java 9 and may not work. 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;
    }
}

Instead of using URL class loader you can try using custom class loader to load class using its fully qualified name. 您可以尝试使用自定义类加载器来使用其完全限定名称加载类,而不是使用URL类加载器。 Using this approach you should be able to bypass the class loading delegation to the parent class loader which is causing problem in your case. 使用这种方法,您应该能够绕过向父类加载器的类加载委派,这会导致您的问题。 So your class loader should be able to load the class from libjar-2.0.0.jar. 因此,您的类加载器应该能够从libjar-2.0.0.jar加载类。

暂无
暂无

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

相关问题 Java Classloader - 如何引用jar的不同版本 - Java Classloader - how to reference different versions of a jar Java如何使用System ClassLoader(无URLClassLoader)从类路径中的jar中加载类? - Java How to load classes out of a jar in the classpath with the System ClassLoader (no URLClassLoader)? 如何允许类加载器从已更改的jar加载类? - How to allow the classloader to load classes from a changed jar? 如何使用 ClassLoader 从另一个 jar 加载 bean XML 文件 - How to load the bean XML file from another jar using ClassLoader Jar hell:如何在运行时使用类加载器将一个jar库版本替换为另一个jar库版本 - Jar hell: how to use a classloader to replace one jar library version with another at runtime 在运行时加载和卸载不同版本的jar - Load and unload different version of jar at runtime 如何在非Eclipse项目中使用Eclipse Jar-in-Jar Classloader在运行时加载jar? - How to use the eclipse Jar-in-Jar Classloader in a non-eclipse project to load jars at runtime? 从JAR加载资源而不访问ClassLoader? - Load resource from JAR without accessing ClassLoader? 多个ClassLoader如何从不同位置加载同名资源? - How to load resources with the same name from different locations in multiple ClassLoader? 当ClassLoader在EJB jar中并且属性文件处于战争状态时,如何加载属性文件 - How to load a Properties File when the ClassLoader is in the EJB jar and the Properties File is in war
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM