美文网首页
JDK源码(二)ClassLoader之双亲委托模型

JDK源码(二)ClassLoader之双亲委托模型

作者: 李亚林1990 | 来源:发表于2019-03-15 15:08 被阅读0次

    今天偶然看到一篇Java技术栈发表的文章,关于自定义类加载器的实现感觉有点问题。在此做一个梳理。
    原文链接:https://www.jianshu.com/p/e808ed28a5d6
    本文代码示例来自原文,稍作修改。
    我们看看原文的例子:

    image.png
    疑问:第一时间感觉两处“return super.loadClass(name);”应该直接“return null”,不然在找不到类的情况下会死循环?!

    接下来做了一番分析和验证。
    自定义加载器使用中我们调用ClassLoader.loadClass加载一个类,来loadClass关于类加载双亲委托模型的实现:

        public Class<?> loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false);
        }
        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                //判断当前类是否已经被加载,调用本地方法findLoadedClass0
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        //双亲委托模型:父加载器不为空,调用父加载器加载,否则查看启动类加载器是否已加载该类
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            //调用本地方法findBootstrapClass
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
                    //父加载器或者启动类加载器都未加载该类,则调用本加载器的findClass方法
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);
    
                        // 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();
                    }
                }
                //是否链接(验证、准备、解析)
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    

    findClass在ClassLoader中并未实现,须用户在自定义加载器中实现:

        protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }
    

    图解双亲委托模型:


    image.png

    一句话总结:自底向上委托,自顶向下加载。

    我们再回过头来分析找不到类的情况下代码的调用过程:
    LocalClassLoader.loadClass => 父加载器返回null => LocalClassLoader.findClass失败 => super.loadClass === LocalClassLoader.loadClass
    代码进入死循环

    本地验证:

    public class LocalClassLoader extends ClassLoader {
        private String path = "F:/study/";
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            Class<?> cls = findLoadedClass(name);
            if (cls != null) {
                return cls;
            }
    
            if (!name.endsWith(".Key")) {
                return super.loadClass(name);
            }
    
            try {
                InputStream is = new FileInputStream(path + name.replace(".", "/") + ".class");
                byte[] bytes = IOUtils.readNBytes(is, is.available());
                return defineClass(name, bytes, 0, bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return super.loadClass(name);
        }
    
        public static void main(String[] args) {
            try {
                LocalClassLoader lcl = new LocalClassLoader();
                Class<?> cls = lcl.loadClass("com.yalin.test.Key");   //1
                //Class<?> cls = lcl.loadClass("com.yalin.test.jdk.Key");  //2
                Field field = cls.getDeclaredField("key");
                field.setAccessible(true);
                Object value = field.get(cls.newInstance());
                System.out.println(value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    加载F:/study/目录存在的类com.yalin.test.Key,输出成功:


    image.png

    加载F:/study/目录不存在的类com.yalin.test.jdk.Key,代码进入死循环:


    image.png

    转载请备注原文链接。

    相关文章

      网友评论

          本文标题:JDK源码(二)ClassLoader之双亲委托模型

          本文链接:https://www.haomeiwen.com/subject/qlilmqtx.html