Java类加载器何时参与?

时间:2012-07-18 15:37:19

标签: java classloader

Java类加载器上有1000万篇文章和文档, / *为什么*编写自己的文章...但它们似乎都是假设有些东西我找不到简单的答案!

我理解类加载器的工作:读取字节码并从中构造一个对象。不同的类加载器以不同的方式执行此操作等。

但是从来没有必须在我自己的代码中对类加载器API进行编码,而且从不必编写自己的代码API,在ClassLoader自己的代码时,我很难理解实际上是火了。

例如:

public static void main(String[] args) {
    Fizz fizz = new Fizz();
    fuzz.buzz();
}

在这里,我们有一个Fizz对象。在可以实例化Fizz之前,我们需要一个类加载器来启动并将Fizz.class加载到其缓存中。 这种情况何时何地发生?!?!它在我的代码中没有明确表示所以它必须隐含在JRE的某个地方......?

与该问题相关,如果我编写自己的类加载器,比如WidgetClassLoader,并希望将其配置为加载我的所有应用程序的类,或者可能只是我的Fizz.class,我该如何“绑定“这个WidgetClassLoader进入我的应用程序,以便它知道要使用哪个类加载器?我的代码是否需要显式调用此类加载器,还是像第一个示例一样隐式?提前谢谢!

4 个答案:

答案 0 :(得分:7)

你的问题并不像你现在想象的那么简单。

你的Fizz例子: Fizz什么时候装?这在JLS (Chapter 5.4: Linking)中定义。它没有定义何时加载Fizz,但它保证了可见行为。对于'when'部分,如果找不到Fizz,则会从访问Fizz的第一个语句(Fizz fizz = new Fizz())中抛出异常。我很确定在这种情况下它将是新的Fizz(),因为表达式的右侧首先被评估。如果你这样写的话:

Fizz fizz = null;
fizz = new Fizz();

在这种情况下,Fizz fizz = null会抛出异常,因为它是第一次访问Fizz类。

谁加载Fizz?当必须加载类时,使用“属于”需要该类的代码的类加载器来获取该类。在Fizz示例中,这将是使用main方法加载类的类加载器。当然,如果类加载器无法自行加载Fizz,则可以选择委托给它的父类加载器。

如何让JVM使用我的 ClassLoader?有两种方式,明确或隐含。明确地说:您可以通过调用其方法通过您自己的类加载器加载类。 Implcitly:当你从已经从类加载器加载的类中执行代码(意思是方法或初始化器)并且需要在进程中解析类引用时,你的类加载器将被自动使用,因为它是加载代码的类加载器第一名。

答案 1 :(得分:3)

Java有一个默认的类加载器。这将在默认类路径中查找类声明。如果您编写自己的类加载器,则可以(并且应该)设置父类加载器。如果您没有其他的,这将是默认值。如果你不这样做,你的类加载器将无法找到java API类。如果java查找一个类,它不会开始查找自定义类加载器,而是使用父类加载器。如果这个有父母,那么就从那里开始,依此类推。只有在找不到类时,才会使用下一个子类加载器再次尝试它。只要有孩子,这种情况就会持续下去。如果链中的任何加载器都找不到该类,则抛出ClassNotFoundException

当然,如果您首先将其设置为默认类加载器(通过调用Thread.currentThread().setContextClassLoader())或手动加载类(通过调用loadClass()),则java仅使用您的类加载器。

我不确定何时调用类加载器。我认为它是在启动程序(在所有声明为import的类上)或首次使用类(变量声明或构造函数调用)时调用的。

答案 2 :(得分:0)

该课程的实际创建发生在defineClass。该类是使用来自多个源中的任何一个的字节数组创建的。

到达defineClassprotected)的正常路径是findClass(当然,也是protected)。所以通常的切入点是loadClass - > findClass - > defineClass。但是特殊情况还有其他途径。

(整个过程非常复杂,代表了添加图层的历史,因为保护变得更加复杂,访问模式也变得更加多样化。)

答案 3 :(得分:0)

如果您对类加载器以及它们何时以及如何工作感兴趣,您还可以查看the OSGi specification - 在我看来,这对您来说将是一个非常有趣的读物。 OSGi是一个Java框架,它提供模块化,清晰的代码分离和生命周期管理,目前非常流行(例如Eclipse本身基于一个)。

OSGi大量使用类加载器,并且有很好的解释,何时以及如何在规范内部发生类加载。基本上他们为每个bundle都有一个单独的bundle类加载器(这就是调用模块的方式),这些类加载器负责处理依赖关系并从另一个bundle中获取正确的类。