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

Posted master-dragon

tags:

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

目录

java.lang.NoClassDefFoundError 问题引入

工程的3个模块介绍

源码见:https://github.com/doctording/bundle

  1. bundle-main 测试
  2. demo1 有一个Hello类
  3. demo2 有一个User类
    • User类使用Hello类
    • demo2模块依赖demo1且scope是provided

测试例子报错java.lang.NoClassDefFoundError

Exception in thread "main" java.lang.NoClassDefFoundError: com/dq/bundle/demo1/Hello
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at com.dq.bundle.main.Main.loaderErrorTest(Main.java:55)
	at com.dq.bundle.main.Main.main(Main.java:23)
Caused by: java.lang.ClassNotFoundException: com.dq.bundle.demo1.Hello
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 7 more

附:测试代码 & 原因

static void loaderErrorTest() throws
            MalformedURLException,
            ClassNotFoundException,
            IllegalAccessException,
            InstantiationException,
            NoSuchMethodException,
            InvocationTargetException
    
        Main m = new Main();
        String filePath1 = "/Users/mubi/IdeaProjects/bundle/demo1/target/demo1-1.0-SNAPSHOT.jar";
        URLClassLoader ul1 = new URLClassLoader(m.convert2URLArray(filePath1));

        String filePath2 = "/Users/mubi/IdeaProjects/bundle/demo2/target/demo2-1.0-SNAPSHOT.jar";
        URLClassLoader ul2 = new URLClassLoader(m.convert2URLArray(filePath2));

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

        /**
         * ul2 正常加载User类,但是无法加载其依赖的 Hello类,会报错ClassNotFoundException
         */
        Class<?> userClass = ul2.loadClass("com.dq.bundle.demo2.User");
        Object userInstance = userClass.newInstance();
        Method welcomeMethod = userClass.getMethod("welcome", helloClass);
        welcomeMethod.invoke(userInstance, helloInstance);
    

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

原因:

Hello的类加载器cl1和User的类加载器cl2不一样;模块依赖使用的是provided;显然cl2是无法加载Hello类的,就会报错java.lang.NoClassDefFoundError

使用自定义类加载器解决

User的类加载器无法加载hello,可以在User的类加载器中引入Hello的类加载器,这样在一个模块中完成对User和Hello两个类的加载(即这里有一个新的自定义类加载器, 能实现对不同类的加载)

static void loaderErrorTest() throws
            MalformedURLException,
            ClassNotFoundException,
            IllegalAccessException,
            InstantiationException,
            NoSuchMethodException,
            InvocationTargetException
    
        Main m = new Main();
        String filePath1 = "/Users/mubi/IdeaProjects/bundle/demo1/target/demo1-1.0-SNAPSHOT.jar";
        URLClassLoader ul1 = new URLClassLoader(m.convert2URLArray(filePath1));

        String filePath2 = "/Users/mubi/IdeaProjects/bundle/demo2/target/demo2-1.0-SNAPSHOT.jar";
        URLClassLoader ul2 = new URLClassLoader(m.convert2URLArray(filePath2));

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

        /**
         * ul2 正常加载User类,但是无法加载其依赖的 Hello类,会报错ClassNotFoundException
         */
        Class<?> userClass = ul2.loadClass("com.dq.bundle.demo2.User");
        Object userInstance = userClass.newInstance();
        Method welcomeMethod = userClass.getMethod("welcome", helloClass);
        welcomeMethod.invoke(userInstance, helloInstance);
    

    static void selfLoaderTest() throws
            MalformedURLException,
            ClassNotFoundException,
            IllegalAccessException,
            InstantiationException,
            NoSuchMethodException,
            InvocationTargetException
    
        Main m = new Main();
        String filePath1 = "/Users/mubi/IdeaProjects/bundle/demo1/target/demo1-1.0-SNAPSHOT.jar";
        URLClassLoader ul1 = new URLClassLoader(m.convert2URLArray(filePath1));

        String filePath2 = "/Users/mubi/IdeaProjects/bundle/demo2/target/demo2-1.0-SNAPSHOT.jar";
        SelfClassLoader ul2 = new SelfClassLoader(m.convert2URLArray(filePath2), ul1);

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

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

    /**
     * 自定义类加载器,可以传入其它类加载器
     */
    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;
    

使用arthas查看类加载信息

  • Hello
  • User
  • User类的方法查看

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

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

java 双亲委派机制 & 与打破

Java类加载-双亲委派

java虚拟机——双亲委派模型

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

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