1

I have this code

        MyClassloader loader = new MyClassloader();
        Class c = loader.loadClass(classToLoad);
        Thread.currentThread().setContextClassLoader(loader);
        MyClass myClass = (MyClass) c.newInstance();

And classloader code is

public MyClassloader) throws IOException {
    super(Thread.currentThread().getContextClassLoader());
}

The class loading works fine the problem is that static variables are not isolated, so if inside the MyClass a static variable is set all other MyClass instances get the same value.

In what can a static variable be isolated?

quarks
  • 33,478
  • 73
  • 290
  • 513

1 Answers1

2

You delegate to the same class loader. So you will see no difference.

What should yo do? Implement class loading in your class loader, don't just delegate. For instance, inherit your class loader from URLClassLoader, initialize it properly (it means, provide a valid class path to your class loader to initialize it). Then you will see, that classes loaded by you class loader are not euqal to the classes loaded by the standard class loader, and as a consequence, their static members will be different.

Often the context class loader is inherited from URLClassLoader and thus you don't have to spend much time configuring class path for it. Instead, to initialize you class loader you can just reuse the URLs of the context class loader, as follows:

public class MyIndependentClassLoader extends URLClassLoader {
  public MyIndependentClassLoader(){
    super(((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs());
  }
}

Now you can use your class loader and check static members.

What is the difference between this approach and the original approach?

You know that classes are also objects (of special type): they have their own member variables, name, serialVersionUID, annotationData, etc., and their own methods, newInstance(), getFields(), getMethods(), etc. When you call

Class c = loader.loadClass("com.my.package.MyClassX");

you get c, this is an object that describes the loaded class "some class name". This c not only allows to create instances, but holds also all static members.

In your code: Your class loader does not load class directly. Instead, it delegates to the context class loader. That's why if you compare two loaded classes

Class c = loader.loadClass("com.my.package.MyClassX");
Class ct = Thread.currentThread().getContextClassLoader().loadClass("com.my.package.MyClassX");

you will see, that c and ct is the same object. If you call c.equals(ct), it will give you true. And if you call c == ct, it will also give you true. It means, this is the same class instance. That's why - naturally - if you check static variables, they will be the same. If you change static variable in one class, it will also be changed in another class, and vice versa.

In my code: The essential difference is, that the class loader loads classes itself. It does not delegate it to other class loader. I suggested to extend it from URLClassLoader to simplify our implementation (otherwise you would have implement from scratch dealing with class path, directories, jars, visibility, caching, etc.). And to avoid adding each class path element step by step I suggested to use the same class path (the same list of directories, jars, etc.) as the context class loader. So our class loader will be able to find the same classes as the context class loader. Now check the class that this class loader loads. Let's call it ci:

Class ci = new MyIndependentClassLoader().loadClass("com.my.package.MyClassX");

If you compare these classes, you will see that c.equals(ci) gives false. Means, they are different. That's why they have also independent static members. If you change static member in one class, ct, it will not change in the other, ci, and vice versa.

mentallurg
  • 4,967
  • 5
  • 28
  • 36
  • What is the difference from doing `extends ClassLoader` and `extends URLClassLoader` in your example? – quarks Aug 25 '19 at 00:12
  • The reason is to simplify the loading of classes. Of course you can do that without using the URLClassLoader. Then you have to implement Resolving / Loading of classes (how will your class loader load byte code for class X?) – mentallurg Aug 25 '19 at 01:04
  • I mean I don't understand what is the difference of your code to my code in relation to "Static Variable Isolation"? – quarks Aug 25 '19 at 09:30
  • Briefly: your class loader returns *the same* class instance as the context class loader. There is no isolation, because this is the same class instance: the same place in memory, the same static members, etc. To reach isolation, don't delegate to another class loader, but load classes directly, "by your own hands". Then you will get 2 different class instance with completely independent static members. I added more details to my answer above. You class loader is just a "short cut" for context class loader. It provides absolutely the same results as the context class loader. – mentallurg Aug 25 '19 at 10:51
  • My actual classloader code is `public class MyClassloader extends ClassLoader { public MyClassloader() throws IOException { super(Thread.currentThread().getContextClassLoader()); } }` Can you point out how this is different from your example? And how this is not delegating as opposed to your example. – quarks Aug 28 '19 at 11:20
  • The difference is huge. In your constructor you set parent to the context class loader to the thread context class loader. Now look at the implementation of ClassLoader.loadClass(). The most important lines there the following once: if `(parent != null) { ... c = parent.loadClass(name, false); } else {`. Means, your `MyClassloader` *doesn't load* classes, it just **delegates** to its parent. And this parent you set to the thread context class loader. That's why your class loader returns absolutely the same as the context class loader. – mentallurg Aug 29 '19 at 00:03
  • But in my case it is *completely* different. I don't set any parent class loader and thus I don't delegate to the thread context class loader. My class loader performs all the "hard work" by itself. That's why it loads its *own* instance of the request class and that's why this instance is different from what the thread context class loader loads. – mentallurg Aug 29 '19 at 00:07