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

时间:2016-06-20 02:54:36

标签: 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("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();
     } 
}

这是我的自定义classLoader:

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类?

2 个答案:

答案 0 :(得分:1)

请参阅此SO文章:Difference between thread's context class loader and normal classloader

重要的一点是,虽然要求您的类加载器加载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());
    }
}

输出(使用jdk1.8.0_91)

MyClassLoader(sun.misc.Launcher$AppClassLoader@18b4aac2)
MyClassLoader.loadClass("HelloMain")
Hello from HelloMain
  Loaded by sun.misc.Launcher$AppClassLoader@18b4aac2
  Context class loader: MyClassLoader@6d06d69c
Foo loaded
Hello from Foo
  Loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2
  Context class loader: MyClassLoader@6d06d69c
Bar loaded
Hello from Bar
  Loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2
  Context class loader: MyClassLoader@6d06d69c

答案 1 :(得分:0)

public class MyClassLoader extends URLClassLoader {

    public MyClassLoader() throws Exception{
         super(new URL[]{new URL("file:///home/ubuntu/java/hello.jar")});
    }

    @Override
    public Class loadClass(String name) throws ClassNotFoundException {
        if (name.equals("hello.example.org.Foo") || name.equals("hello.example.org.Bar")) 
        {
            System.out.println("Foo or Bar is loaded");
        }
        return super.loadClass(name);

    }
}