我如何知道java中当前的classLoader

时间:2016-12-12 14:03:36

标签: java jvm classloader

我正在学习加载java类的过程并遇到一些困惑。

我知道在加载 java类时,当前的classLoader 不会直接加载 java类,它会委托给它的父级classLoader (一个递归过程),直到它父加载这个类为止。

问题是当前的classLoader是什么? Bootstrap?延期? App?
如何获取当前的classLoader? 我知道有一个API:

xxx.Class.getClassLoader();

但我不确定返回值是否为 currentClassLoader 。我认为它应该是classLoader,它实际上加载了这个 java类

为了更详细地描述我的问题,我将举例说明。
我在博客中获得了以下内容。

  

ThreadContextClassLoader 用于处理 java SPI ,接口在java core lib中定义并由 Bootstrap ClassLoader 加载party实现这些接口然后jar由 AppClassLoader 加载   解决方案:传统的classLoader无法处理这种情况,因为当我们在核心库中使用第三方工具时,它无法发现第三方jar。

以上大部分内容我能理解,但解决方案让我感到困惑: 例如,接口 CoreA 和类 CoreB 位于java core lib中,应由 Bootstrap ClassLoader 加载AImpl 是第三方的 A 的实现,应该由 AppClass loader 加载。

代码段如下:

 public Interface CoreA{
     void add();
 }

 public Interface AImpl implements CoreA{
     void add(){};
 }

 public class B{
      public void demo(){
         a = new AImpl();
      }
 }

然后,如果我们在B中引用main method,那么我们将加载B,因为B类加载器 Bootstrap 然后关于AImpl当前Loader是 Bootstrap 所以无法找到它? 我不知道这是否与我猜的一样?

任何建议都将受到赞赏。

3 个答案:

答案 0 :(得分:4)

一般来说,你是对的,它无法找到。我将向您展示以下示例。我们假设我们有3个类:ABMain,如下所示:

public class A {
    public String a() {
        return "a";
    }
}

public class B {
    public String b() {
        return new A().a();
    }
}

public class Main {
    public static void main(String... args) {
        System.out.println(new B().b());
    }
}

我们将这些类打包到对应的jar:a.jarb.jar并将Main.class放入工作目录。之后,让我们测试以下场景:

1)所有内容(A.classB.classMain.class)都由system classloader加载并正常运行:

$ java -cp .:a.jar:b.jar Main
a

2)B.classsystem classloader加载,A.classbootstrap classloader加载,一切正常,因为system classloader委托加载到bootstrap classloader (只是因为bootstrap classloader可以加载它):

$ java -Xbootclasspath/a:a.jar -cp .:b.jar Main
a

3)A.class加载system classloaderB.class加载bootstrap classloader(您的情况)。在此方案中,加载B.class当前类加载器的过程为bootstrap classloader,但无法找到B.class并失败:

$ java -Xbootclasspath/a:b.jar -cp .:a.jar Main
Exception in thread "main" java.lang.NoClassDefFoundError: A
        at B.b(B.java:4)
        at Main.main(Main.java:4)

让我们更仔细地看一下最后一个例子。这里发生了什么:

  1. 尝试使用public static main(String[] args)方法

    查找课程

    1.1。 system classloader尚未加载它,因此委托给extension classloader

    1.2。 extension classloader尚未加载它,因此委托给bootstrap classloader

    1.3。 bootstrap classloader尚未加载它并尝试加载,它无法加载它并将控制权返还给extension classloader

    1.4。 extension classloader尝试加载,无法加载并将控件返回system classloader

    1.5。 system classloader已加载Main.class

  2. Main.class已处理,我们尝试使用当前的classloader B.class加载system classloader

    2.1。 system classloader尚未加载它,因此委托给extension classloader

    2.2。 extension classloader尚未加载它,因此委托给bootstrap classloader

    2.3。 bootstrap classloader尚未加载它并加载B.class

  3. 处理了
  4. B.class,我们尝试使用当前的类加载器加载A.class bootstrap classloader

    3.1。 bootstrap classloader尚未加载它并尝试加载和 失败

  5. 我希望它会对你有所帮助。

答案 1 :(得分:0)

  

当前的班级加载者'是引用它的类的真正的类加载器(实际加载此类)。

e.g。 如果class A classLoader ext classloader class A引用了类B C D。然后是当前的类加载器' B C D ext classLoader 。当然还有现在的班级负责人#39; 主类 System classLoader

答案 2 :(得分:0)

当类A尝试加载另一个类B时,加载A的ClassLoader是当前的ClassLoader。当前一词模糊地指执行上下文-例如您如何最终获得触发当前类加载调用的方法。

没有一种方法(例如getCurrentClassLoader)可以简单地给出当前的ClassLoader,但是有一些api方法在内部使用了当前ClassLoader的概念。例如,Class.forName(String className)

如果您检查该方法的实现方式,它将告诉您“当前类加载器”的含义:

public static Class<?> forName(String className) throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

如果您可以持有Class实例,则始终可以通过调用Class::getClassLoader()方法来请求其背后的加载器。那将是您当前的类加载器。但是,棘手的一点是,确定加载程序是引导程序,扩展程序还是系统类加载程序。棘手的原因是它是特定于实现的,并且您始终可以实现自己的类加载机制。

@dmitrievanthony给出的示例是使事情变得非常复杂的示例。 JNDI面临着类似的情况,以及引入了Thread.getContextClassLoader() hack的原因。进一步了解here

引用文章中最相关的文章:

  

...根据定义,当前的类加载器将加载并定义当前方法所属的类。当类之间的动态链接在运行时解析时,以及使用Class.forName(),Class.getResource()和类似方法的单参数版本时,将暗含该类加载器。 X.class类文字等语法结构也使用它。