Java 双亲委派问题&解决实战

Posted master-dragon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 双亲委派问题&解决实战相关的知识,希望对你有一定的参考价值。

接上一篇: Java 双亲委派问题&解决实战(一),讨论依赖中的类加载问题

设想如下场景

  • demo1 的1.0;2.0;3.0 三个版本分别被其它模块依赖;如果业务bundle-main要正常运行依赖结果,这显然要冲突

    项目工程如下:

    经过上一篇文章,显然只要我们自定义类加载器,每个模块类正确加载对应版本的即可,代码如下
package com.dq.bundle.main;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @Author mubi
 * @Date 2021/3/13 10:47
 */
public class Main 
    public static void main(String[] args) throws
            MalformedURLException,
            ClassNotFoundException,
            IllegalAccessException,
            InstantiationException,
            NoSuchMethodException,
            InvocationTargetException 
        Main m = new Main();
        String hello1Path = "/Users/mubi/IdeaProjects/bundle/demo2/src/main/lib/demo1-1.0-SNAPSHOT.jar";
        String hello2Path = "/Users/mubi/IdeaProjects/bundle/demo3/src/main/lib/demo1-2.0-SNAPSHOT.jar";
        String hello3Path = "/Users/mubi/IdeaProjects/bundle/demo1/target/demo1-3.0-SNAPSHOT.jar";
        URLClassLoader helloClassLoader1 = new URLClassLoader(m.convert2URLArray(hello1Path));
        URLClassLoader helloClassLoader2 = new URLClassLoader(m.convert2URLArray(hello2Path));
        URLClassLoader helloClassLoader3 = new URLClassLoader(m.convert2URLArray(hello3Path));


        /**
         * 正常加载Hello类 版本3
         */
        Class<?> helloClass3 = helloClassLoader3.loadClass("com.dq.bundle.demo1.Hello");
        Object helloInstance = helloClass3.newInstance();
        Method sayMethod = helloClass3.getMethod("say", String.class);
        sayMethod.invoke(helloInstance, "world");

        // User类使用 Hello 版本1
        String userPath = "/Users/mubi/IdeaProjects/bundle/demo2/target/demo2-3.0-SNAPSHOT.jar";
        SelfClassLoader userLoader = new SelfClassLoader(m.convert2URLArray(userPath),
                helloClassLoader1);

        // Teacher类使用 Hello 版本2
        String teacherPath = "/Users/mubi/IdeaProjects/bundle/demo3/target/demo3-3.0-SNAPSHOT.jar";
        SelfClassLoader teacherLoader = new SelfClassLoader(m.convert2URLArray(teacherPath),
                helloClassLoader2);


        /**
         *  正常加载User类, 并使用传递过来的 helloClassLoader1 正常加载Hello类
         */
        Class<?> helloClass1 = helloClassLoader1.loadClass("com.dq.bundle.demo1.Hello");
        Object helloInstance1 = helloClass1.newInstance();
        Class<?> userClass = userLoader.loadClass("com.dq.bundle.demo2.User");
        Object userInstance = userClass.newInstance();
        Method welcomeMethod = userClass.getMethod("welcome", helloClass1);
        welcomeMethod.invoke(userInstance, helloInstance1);

        /**
         * 正常加载Teacher类,并使用传递过来的 helloClassLoader2 正常加载Hello类
         */
        Class<?> helloClass2 = helloClassLoader2.loadClass("com.dq.bundle.demo1.Hello");
        Object helloInstance2 = helloClass2.newInstance();
        Class<?> teacherClass = teacherLoader.loadClass("com.dq.bundle.demo3.Teacher");
        Object teacherInstance = teacherClass.newInstance();
        Method welcomeTeacherMethod = teacherClass.getMethod("welcome", helloClass2);
        welcomeTeacherMethod.invoke(teacherInstance, helloInstance2);
    

    /**
     * 自定义类加载器,可以传入其它类加载器
     */
    static class SelfClassLoader extends URLClassLoader 
        private ClassLoader parentClassLoader;

        public SelfClassLoader(URL[] urls, ClassLoader classLoader) 
            super(urls);
            this.parentClassLoader = classLoader;
        

        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException 
            Class<?> clazz = loadFromParent(name);
            if (clazz != null) 
                return clazz;
            
            return super.loadClass(name);
        

        public Class<?> loadFromParent(String name) 
            // 从当前classLoader加载
            Class<?> loadClass = super.findLoadedClass(name);

            try 
                if (loadClass == null) 
                    // 用父加载加载,这里是自己传入的类加载器
                    loadClass = this.parentClassLoader.loadClass(name);
                
             catch (ClassNotFoundException e) 
//                e.printStackTrace();
            
            return loadClass;
        
    

    private URL[] convert2URLArray(String filePath) throws MalformedURLException 
        File f = new File(filePath);
        URL[] urls = new URL[1];
        urls[0] = f.toURI().toURL();
        return urls;
    


正常运行如下

附:多版本类加载问题


如上可以正常运行3个demo1版本,本质原因:类加载器 + 类全限定名 才是确定一个类的;我们都是com.dq.bundle.demo1.Hello这个类限定名,但是我们有3个不同的类加载器,这就让我们能够在一个工程中同时使用3个版本的demo1

String hello1Path = "/Users/mubi/IdeaProjects/bundle/demo2/src/main/lib/demo1-1.0-SNAPSHOT.jar";
String hello2Path = "/Users/mubi/IdeaProjects/bundle/demo3/src/main/lib/demo1-2.0-SNAPSHOT.jar";
String hello3Path = "/Users/mubi/IdeaProjects/bundle/demo1/target/demo1-3.0-SNAPSHOT.jar";
URLClassLoader helloClassLoader1 = new URLClassLoader(m.convert2URLArray(hello1Path));
URLClassLoader helloClassLoader2 = new URLClassLoader(m.convert2URLArray(hello2Path));
URLClassLoader helloClassLoader3 = new URLClassLoader(m.convert2URLArray(hello3Path));

而所谓的类加载则是自己重写了loadClass方法实现的。

以上是关于Java 双亲委派问题&解决实战的主要内容,如果未能解决你的问题,请参考以下文章

Java 双亲委派问题&解决实战

Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里?

Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里?

面试必备:什么时候要打破双亲委派机制?什么是双亲委派? (图解+秒懂+史上最全)

面试必备:什么时候要打破双亲委派机制?什么是双亲委派? (图解+秒懂+史上最全)

java 双亲委派机制 & 与打破