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

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

JDK源码(二)ClassLoader之双亲委托模型_第1张图片
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);
    }

图解双亲委托模型:


JDK源码(二)ClassLoader之双亲委托模型_第2张图片
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,代码进入死循环:


JDK源码(二)ClassLoader之双亲委托模型_第3张图片
image.png

转载请备注原文链接。

你可能感兴趣的:(JDK源码(二)ClassLoader之双亲委托模型)