我正在学习加载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 所以无法找到它?
我不知道这是否与我猜的一样?
任何建议都将受到赞赏。
答案 0 :(得分:4)
一般来说,你是对的,它无法找到。我将向您展示以下示例。我们假设我们有3个类:A
,B
和Main
,如下所示:
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.jar
,b.jar
并将Main.class
放入工作目录。之后,让我们测试以下场景:
1)所有内容(A.class
,B.class
,Main.class
)都由system classloader
加载并正常运行:
$ java -cp .:a.jar:b.jar Main
a
2)B.class
由system classloader
加载,A.class
由bootstrap classloader
加载,一切正常,因为system classloader
委托加载到bootstrap classloader
(只是因为bootstrap classloader
可以加载它):
$ java -Xbootclasspath/a:a.jar -cp .:b.jar Main
a
3)A.class
加载system classloader
,B.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)
让我们更仔细地看一下最后一个例子。这里发生了什么:
尝试使用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
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
B.class
,我们尝试使用当前的类加载器加载A.class
bootstrap classloader
3.1。 bootstrap classloader
尚未加载它并尝试加载和
失败
我希望它会对你有所帮助。
答案 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类文字等语法结构也使用它。