【问题标题】:how is the control flow to findClass offindClass 的控制流程如何
【发布时间】:2016-06-07 18:09:46
【问题描述】:

在加载类的父委托模型中,我知道在父类上调用 loadclass(),一直到类加载器层次结构的顶部(假设未加载类)。此时最顶层的父类加载器的 findClass 被调用。 如果没有找到这个类,如何将控制权转移到下一个类加载器的findClass方法?

【问题讨论】:

    标签: java delegation classloader


    【解决方案1】:

    findClass(String) 将被类加载器的loadClass(String) 方法调用。这是默认的 实现抛出 ClassNotFoundException 并打算被类加载器覆盖。

    loadClass(String) 方法会按顺序调用以下方法

    • 首先它会尝试查找类是否已加载:findLoadedClass(String)
    • 如果没有找到,它会调用父类加载器loadClass(String) 方法。
    • 如果没有找到,会调用findClass(String)方法(自定义加载)

    所以自定义类加载器所要做的就是覆盖findClass(String) 方法以自定义方式加载类。这将确保类加载中的正确委派。检查链接(javadoc),它解释了采取了哪些步骤以及如何从loadClass(String)调用findClass(String)

    因此类加载按以下顺序进行(示例) ClassLoader A with parent B (只解释 findClass 和 loadClass)

                   A.loadClass()
                        |
                    (not-found?) (by findLoadedClass)
                        |
                   B.loadClass()
                        |
                    (not found?) (by findLoadedClass)
                        |
             systemclassloader.loadClass()  (Bs parent, also can be 
                        |                    called classpath classloader)
                        |
                    (not found?) (by findLoadedClass)
                        |
            bootstrap classloader.loadClass() (the bootstrap classloader, 
                        |                      this has no parent)
                        |
                    (not found?)
                        |
             systemclassloader.findClass()  (on system classloader, 
                        |                    will try to "find" class in "classpath")
                        |
                    (not found?)
                        |
                    B.findClass()
                        |
                    (not found?)
                        |
                    A.findClass()
                        |
                     (not found?)
                        |
                ClassNotFoundException
    

    在任何给定时间,如果找到类(通过 findClass 或 findLoadedClass),则返回该类。

    【讨论】:

    • 不错。我想我没有想清楚 - 你很好地说明了这一点。在您的图表中不是很明确但可能是隐含的并且对我来说缺少的拼图是每次控制都从具有 null 的祖先类加载器的 loadClass 返回,以前的类加载器调用 findClass 方法。是否要求每个 findClass 方法都返回一个有效的类或抛出一个 ClassNotFoundException ?
    • 谢谢,是的,它要求它要么返回类,要么抛出 ClassNotFoundException,否则你会得到一个 NullPointerException
    • 在相关的注释中——你知道类加载器加载的类不能重新加载的限制来自哪里吗?我在规范中没有看到任何提及...是什么阻止了类加载器覆盖 loadClass 并且在继续加载另一个版本之前不检查 findLoadedClass ?
    • 对此没有限制,如果没有热部署等功能。它只是记录了通常应该遵循这些规则,但这不是规则。您的应用程序可能需要以完全不同的顺序加载类。这就是它的魔力!这就是为什么我们有像 osgi、webapps 这样的框架和像热部署这样很酷的东西:)
    • 给定同一个类,调用findLoadedClass,A_classloader和B_classloader会得到不同的结果吗?
    【解决方案2】:

    以上解释全面而优秀。 了解委托类加载原理的更直接的方法是阅读源代码。无论如何,源代码一点也不复杂。

    http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/ClassLoader.java#400

    400     protected Class<?> loadClass(String name, boolean resolve)
    401         throws ClassNotFoundException
    402     {
    403         synchronized (getClassLoadingLock(name)) {
    404             // First, check if the class has already been loaded
    405             Class c = findLoadedClass(name);
    406             if (c == null) {
    407                 long t0 = System.nanoTime();
    408                 try {
    409                     if (parent != null) {
    410                         c = parent.loadClass(name, false);
    411                     } else {
    412                         c = findBootstrapClassOrNull(name);
    413                     }
    414                 } catch (ClassNotFoundException e) {
    415                     // ClassNotFoundException thrown if class not found
    416                     // from the non-null parent class loader
    417                 }
    418 
    419                 if (c == null) {
    420                     // If still not found, then invoke findClass in order
    421                     // to find the class.
    422                     long t1 = System.nanoTime();
    423                     c = findClass(name);
    424 
    425                     // this is the defining class loader; record the stats
    426                     sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
    427                     sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
    428                     sun.misc.PerfCounter.getFindClasses().increment();
    429                 }
    430             }
    431             if (resolve) {
    432                 resolveClass(c);
    433             }
    434             return c;
    435         }
    436     }
    

    【讨论】:

    • 只是想在这里添加一个注释,注意同步块是如何在类名上的,这是java 7中的增强,在java 6之前整个方法是同步的
    猜你喜欢
    • 2019-01-28
    • 2022-01-03
    • 1970-01-01
    • 2019-11-07
    • 2021-09-06
    • 1970-01-01
    • 2019-11-06
    • 2011-10-27
    • 1970-01-01
    相关资源
    最近更新 更多