Java 为什么classLoader只加载主类而不加载任何其他引用类?

Java 为什么classLoader只加载主类而不加载任何其他引用类?,java,Java,假设我有以下几点: public class Foo { static { System.out.println("Foo static initialization is working") } public void sayHello { System.out.println("Hello Foo") } } public class Bar { static { System.out.println("B

假设我有以下几点:

public class Foo {
    static {
       System.out.println("Foo static initialization is working")
    }

    public void sayHello {
       System.out.println("Hello Foo")
    }
}

public class Bar {
    static {
       System.out.println("Bar static initialization is working")
    }

    public void sayHello {
       System.out.println("Hello Bar")
    }
}

public class HelloMain {
     public static void main() {
       Foo foo  = new Foo();
       foo.sayHello();
       Bar bar  = new Bar();
       bar.sayHello();
     } 
}
这是我的自定义类加载器:

public class MyClassLoader extends ClassLoader {
    public MyClassLoader() {
        super(MyClassLoader.class.getClassLoader());
    }

    public MyClassLoader(ClassLoader parent){
        super(parent);
    }

    public Class loadClass(String name) throws ClassNotFoundException {
        System.out.println(name + " ************"); // why Foo and Bar are not coming through here?
        return super.loadClass(name);

    }
}
假设我将所有这些打包为jar,我的jar文件名为“hello.jar” 并运行以下命令:

java -Djava.system.class.loader=MyClassLoader -jar hello.jar
它打印上面代码中的所有标准java类以及HelloMain类,但是没有打印Foo和Bar类。我想知道为什么?另外,如何更改某些内容,以便MyClassLoader中的print语句打印Foo和Bar类?

请参阅本文:

重要的一点是,尽管您的类加载器被要求加载
HelloMain
,但并不是这样做的,因为您只是委托给了父类加载器的超类

由于父类加载器加载了
HelloMain
,因此
HelloMain
引用的所有类也将从父类加载器加载,并且不会询问您的类加载器

类加载器只会被要求为从类加载器加载的类加载引用类

解释为什么,考虑两个具有不同独立类加载器(A和B)的线程。类装入器A不知道如何从B装入类,反之亦然

如果线程A创建了一个对象并将该对象提供给线程B,而JVM随后需要加载引用的类作为线程B执行的操作的结果,那么如果请求线程B的类加载器,它将无法工作。因此,使用的是对象的类加载器,而不是线程的类加载器

为了显示线程类加载器(称为上下文类加载器)和加载类的类加载器之间的差异,我更新了您的代码:

public class MyClassLoader extends ClassLoader {
    public MyClassLoader() {
        super(MyClassLoader.class.getClassLoader());
        System.out.println("MyClassLoader()");
    }
    public MyClassLoader(ClassLoader parent) {
        super(parent);
        System.out.println("MyClassLoader(" + parent + ")");
    }
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        System.out.println("MyClassLoader.loadClass(\"" + name + "\")");
        return super.loadClass(name);
    }
}
public class HelloMain {
    public static void main(String[] args) {
        System.out.println("Hello from HelloMain");
        System.out.println("  Loaded by " + HelloMain.class.getClassLoader());
        System.out.println("  Context class loader: " + Thread.currentThread().getContextClassLoader());
        Foo foo  = new Foo();
        foo.sayHello();
        Bar bar  = new Bar();
        bar.sayHello();
    }
}
public class Foo {
    static {
        System.out.println("Foo loaded");
    }
    public void sayHello() {
        System.out.println("Hello from Foo");
        System.out.println("  Loaded by: " + getClass().getClassLoader());
        System.out.println("  Context class loader: " + Thread.currentThread().getContextClassLoader());
    }
}
public class Bar {
    static {
        System.out.println("Bar loaded");
    }
    public void sayHello() {
        System.out.println("Hello from Bar");
        System.out.println("  Loaded by: " + getClass().getClassLoader());
        System.out.println("  Context class loader: " + Thread.currentThread().getContextClassLoader());
    }
}
见本文:

重要的一点是,尽管您的类加载器被要求加载
HelloMain
,但并不是这样做的,因为您只是委托给了父类加载器的超类

由于父类加载器加载了
HelloMain
,因此
HelloMain
引用的所有类也将从父类加载器加载,并且不会询问您的类加载器

类加载器只会被要求为从类加载器加载的类加载引用类

解释为什么,考虑两个具有不同独立类加载器(A和B)的线程。类装入器A不知道如何从B装入类,反之亦然

如果线程A创建了一个对象并将该对象提供给线程B,而JVM随后需要加载引用的类作为线程B执行的操作的结果,那么如果请求线程B的类加载器,它将无法工作。因此,使用的是对象的类加载器,而不是线程的类加载器

为了显示线程类加载器(称为上下文类加载器)和加载类的类加载器之间的差异,我更新了您的代码:

public class MyClassLoader extends ClassLoader {
    public MyClassLoader() {
        super(MyClassLoader.class.getClassLoader());
        System.out.println("MyClassLoader()");
    }
    public MyClassLoader(ClassLoader parent) {
        super(parent);
        System.out.println("MyClassLoader(" + parent + ")");
    }
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        System.out.println("MyClassLoader.loadClass(\"" + name + "\")");
        return super.loadClass(name);
    }
}
public class HelloMain {
    public static void main(String[] args) {
        System.out.println("Hello from HelloMain");
        System.out.println("  Loaded by " + HelloMain.class.getClassLoader());
        System.out.println("  Context class loader: " + Thread.currentThread().getContextClassLoader());
        Foo foo  = new Foo();
        foo.sayHello();
        Bar bar  = new Bar();
        bar.sayHello();
    }
}
public class Foo {
    static {
        System.out.println("Foo loaded");
    }
    public void sayHello() {
        System.out.println("Hello from Foo");
        System.out.println("  Loaded by: " + getClass().getClassLoader());
        System.out.println("  Context class loader: " + Thread.currentThread().getContextClassLoader());
    }
}
public class Bar {
    static {
        System.out.println("Bar loaded");
    }
    public void sayHello() {
        System.out.println("Hello from Bar");
        System.out.println("  Loaded by: " + getClass().getClassLoader());
        System.out.println("  Context class loader: " + Thread.currentThread().getContextClassLoader());
    }
}

这是有意义的,所以在我的自定义类加载器中,我希望在加载Foo或Bar之前运行一个代码块(比如print语句)。我能做些什么来实现这一点?换句话说,我知道MyClassLoader正在委托给父类加载器,但我如何才能更改它,以便在加载Foo和Bar之前执行一段代码?这有意义吗?你的类加载器被通知/参与加载
Foo
Bar
的唯一方法是让你的类加载器进行实际加载,而不是委托给父类加载器。我以前没有这样做过。这很难做到吗?我不介意写很多代码,我对这个类加载的东西非常感兴趣,因为我以前没有做过,所以我可以看看一些关于如何编写类加载逻辑以实现我的目标的例子吗?如果能看到一个处理几个类的例子,而不是一个显示几十个类的例子,那就太好了。最好从简单开始,这很有意义,所以在我的自定义类加载器中,我想在加载Foo或Bar之前运行一段代码,比如print语句。我能做些什么来实现这一点?换句话说,我知道MyClassLoader正在委托给父类加载器,但我如何才能更改它,以便在加载Foo和Bar之前执行一段代码?这有意义吗?你的类加载器被通知/参与加载
Foo
Bar
的唯一方法是让你的类加载器进行实际加载,而不是委托给父类加载器。我以前没有这样做过。这很难做到吗?我不介意写很多代码,我对这个类加载的东西非常感兴趣,因为我以前没有做过,所以我可以看看一些关于如何编写类加载逻辑以实现我的目标的例子吗?如果能看到一个处理几个类的例子,而不是一个显示几十个类的例子,那就太好了。最好从简单的开始。