1

Say I have the following:

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();
     } 
}

Here is my custom 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);

    }
}

Say I package all this as jar and the name of my jar file be "hello.jar" and run the following command:

java -Djava.system.class.loader=MyClassLoader -jar hello.jar

It prints all the standard java class as well as HelloMain class from the code above however the Foo and Bar classes are not getting printed. I wonder why? Also, how can I change something so that the print statement in MyClassLoader prints Foo and Bar classes?

user1870400
  • 6,028
  • 13
  • 54
  • 115

2 Answers2

1

See this SO article: Difference between thread's context class loader and normal classloader

Important part is that although your classloader is asked to load HelloMain, it isn't the one doing so, because you simply delegated to the superclass, which delegated to the parent classloader.

Since the parent classloader loaded HelloMain, all classes referenced by HelloMain will also be loaded from the parent classloader, and your classloader will not be asked.

Your classloader will only be asked to load referenced classes for classes that was loaded from your classloader.

To explain why, consider two threads with different independent classloaders (A and B). Classloader A doesn't know how to load classes from B, and vice versa.

If thread A creates an object and gives thread B the object, and the JVM then needs to load referenced classes as a result of an action taken by thread B, it wouldn't work if thread B's classloader was asked. So, the classloader of the object is used, not the classloader of the thread.

To show the difference between the thread classloader (called the context classloader) and the classloader that loaded a class, I've updated your code:

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());
    }
}

Output (using 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
Community
  • 1
  • 1
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • This makes sense so basically in my custom class loader I want to run a block of code say a print statement before Foo or Bar is loaded. What is that I can do to accomplish that? – user1870400 Jun 20 '16 at 03:49
  • other words I understand that MyClassLoader is delegating to parent class loader but how can I change it such that I want to execute a block of code prior to loading Foo and Bar? does that make sense? – user1870400 Jun 20 '16 at 04:08
  • The only way for your classloader to be notified/involved with the loading of `Foo` and `Bar`, is for your classloader to do the *actual* loading, instead of delegating to the parent classloader. – Andreas Jun 20 '16 at 05:43
  • I haven't done that before. is that hard to do? I don't mind writing lot of code and I super interested in this class loading stuff as I haven't done it before so can I see some examples somewhere on how to write that class loader logic to achieve my goal? – user1870400 Jun 20 '16 at 05:46
  • It will be great to see an example which deals with few classes rather than a example that shows dozens of classes. it is always better to start simple right. – user1870400 Jun 20 '16 at 05:50
  • If you subclass [`URLClassLoader`](https://docs.oracle.com/javase/7/docs/api/java/net/URLClassLoader.html), you don't have to do much, except set the URL for where to find your classes, basically just pointing to the `hello.jar` file. Works better if your classloader itself is in a different .jar file. – Andreas Jun 20 '16 at 05:50
  • That sounds great! could you please extend the template I had here. http://stackoverflow.com/questions/37915169/how-to-write-a-class-loader-such-that-it-can-execute-a-block-of-code-prior-to-lo Also I dont want to force the Loading of Foo and Bar but rather execute a block of code whenever the Foo and Bar classes are needed to load. I would greatly appreciate for your time! – user1870400 Jun 20 '16 at 05:53
  • No, that is for you to do. This is not a code-writing service. – Andreas Jun 20 '16 at 06:00
  • "please extend the template I had here" aka "please write the code for me", or did I misunderstand that? How should I have taken that comment? – Andreas Jun 20 '16 at 06:07
  • well writing a code so it can help someone understand better is not a bad thing but its your choice to whether you want to do it or not. I have clearly said I have not written a classloader before. When I look at the examples of URLClassLoader it still doesnt quite fit what I was asking for so I would definetly need more pointers. It might very well take me a week to figure out as opposed to learning it from someone..you took it more as like I am delegating work to you as opposed to me requesting for help..so its totally upto you if you want to respond or not as I cant control that anyway. – user1870400 Jun 20 '16 at 06:16
  • I guided you to a class that will do the loading for you, since that is way easier than actually doing the loading yourself. All you have to do is tell it where to find the code to be loaded, in the form of a URL. Is that too difficult to do yourself? --- You already know how to override a method to intercept the load call, so that shouldn't be a problem. --- Writing code yourself will teach you way more than me giving you the code on a platter. – Andreas Jun 20 '16 at 06:25
  • ok I just posted a solution however I am not sure if that is right. – user1870400 Jun 20 '16 at 06:42
-1
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);

    }
}
user1870400
  • 6,028
  • 13
  • 54
  • 115