【问题标题】:how can I know the current classLoader in java我怎么知道java中当前的classLoader
【发布时间】:2018-10-03 20:20:47
【问题描述】:

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

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


问题是当前的classLoader是什么? Bootstrap?扩大?应用程序?
如何获取当前的类加载器?。 我知道有一个 API:

xxx.Class.getClassLoader();

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

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

ThreadContextClassLoader用于处理java SPI,接口在javacore lib中定义,由Bootstrap ClassLoader和第三方实现加载AppClassLoader
解决办法:传统的classLoader无法处理这种情况,因为我们在使用第三方时无法发现第三方jar在核心库中实现。

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

代码段如下:

 public Interface CoreA{
     void add();
 }

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

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

那么如果我们在main method 中引用B,那么我们将加载B,因为B类加载器Bootstrap 然后大约是@987654329 @ 当前的 Loader 是 Bootstrap 所以找不到? 不知道是不是和我猜的一样?

我们将不胜感激。

【问题讨论】:

  • 不同的环境使用不同的类加载策略。默认是先将类加载请求传播到父类加载器。如果无法加载类,这个类加载器会尝试加载它。但是,一些应用服务器类加载器不会先询问它们的父类加载器,如果它们能够自己加载类。接下来,在 OSGi 中还有更复杂的类加载器魔法来实现模块系统。
  • 没有“当前类加载器”这样的东西。每个类都由特定的 ClassLoader 加载。就是这样。
  • 有时我会遇到术语current classloader,这很好,它只是当前执行类的classloader

标签: java jvm classloader


【解决方案1】:

一般来说你是对的,它是找不到的。让我向您展示以下示例。假设我们有 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.classsystem classloader 加载,B.classbootstrap 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 已处理,我们尝试使用当前类加载器 system classloader 加载 B.class

    2.1。 system classloader 还没有加载它,所以委托给extension classloader

    2.2。 extension classloader 还没有加载它,所以委托给bootstrap classloader

    2.3。 bootstrap classloader 还没加载,正在加载 B.class

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

    3.1。 bootstrap classloader 还没有加载它并尝试加载和 失败

希望对你有帮助。

【讨论】:

  • 感谢您的贡献。它对我有很大帮助。但我认为第 2 节 because system classloader delegates loading to bootstrap classloader if can't load class itself 存在一个小缺陷。 系统类加载器将加载委托给它的父类加载器不是因为它不能加载类本身。只有当它的父类加载器不能处理加载java类时,当前类才会尝试加载类本身。
  • 这个答案应该有更多的赞成票。非常有帮助。
  • 后续问题:这意味着如果我在一个加载有 bootstrap classloader 的类中,那么我只能在核心 java 中找到并加载其他类。然而,像ArrayList(据我所知是由bootstrap classloader 加载的)类的实现包括generics
【解决方案2】:

当一个类 A 试图加载另一个类 B 时,加载 A 的 ClassLoader 就是当前的 ClassLoader。当前一词含糊地指代执行上下文-例如你怎么会在触发当前类加载调用的方法中结束。

没有方法——比如getCurrentClassLoader——简单地给出当前的ClassLoader,但是有一些api方法在内部使用了current 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面临的类似情况,也是引入hackThread.getContextClassLoader()的原因。更多详情here

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

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

【讨论】:

    【解决方案3】:

    “当前类加载器”是引用它的类的真正的类加载器(在现实中加载这个类)。

    例如 如果class AclassLoaderext classloader 并且class A 引用类B C D。那么B C D 的“当前类加载器”是ext classLoader。当然,主类的“当前类加载器”是系统类加载器

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-25
      • 2021-10-05
      • 2012-11-01
      • 1970-01-01
      • 2010-12-02
      • 1970-01-01
      • 2015-12-31
      • 2017-02-21
      相关资源
      最近更新 更多