Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load a modified superclass in Java?

Tags:

  • I have a class A extends B.
  • I've created a CustomClassLoader extends ClassLoader to use defineClass(className, byte[], offset, length).
  • I've instanciate a new CustomClassLoader(Thread.currentThread().getContextClassLoader()). So the parent of my CustomClassLoader is the ClassLoader from the current thread.
  • I've modified B class using ASM framework. I've write my modified class in a .class file and use a decompiler to be sure it works. And it works.
  • I've added modified B class to my CustomClassLoader
  • I've set the Thread.currentThread().setContextClassLoader() with my CustomClassLoader.
  • I've load A using Class.forName(String, true, the CustomClassLoader).
  • But the loaded B class seems to be the orginal class.

What did I wrong ? If you need more info, a detailed topic is on my GitHub.

like image 822
A.DUPONCHEL Avatar asked Jul 29 '16 01:07

A.DUPONCHEL


2 Answers

Java classloaders first search the parent classloader before looking in the child.

The loadClass method in ClassLoader performs these tasks, in order, when called to load a class:

  1. If a class has already been loaded, it returns it.
  2. Otherwise, it delegates the search for the new class to the parent class loader.
  3. If the parent class loader does not find the class, loadClass calls the method findClass to find and load the class.

(Understanding Extension Class Loading - Oracle)

If you want to change that order, you need to override the loadClass method as well but there are many caveats and it's not advisable unless you understand classloading very well.

  • The easier option is to make sure that the parent class loader cannot find the original class B.
like image 146
Erwin Bolwidt Avatar answered Sep 28 '22 01:09

Erwin Bolwidt


There are several things to know:

  • For most things, dealing with the thread’s context class loader is obsolete, as it has no impact. It’s more like a convention; setting it has an impact if there’s some other code querying and using it. For the standard class loading process, it doesn’t have any meaning. It’s unfortunate that the documentation doesn’t mention that and make it look like a relevant thing. Perhaps, it was intended to have more meaning when it was added.
  • As pointed out by Erwin Bolwidt, when loading A via your custom loader, it will delegate to its parent loader, returning a class A loaded by the parent.
  • When resolving class references, the JVM will always use the defining loader of the referrer. So when the reference from A to B is resolved, the JVM will always use the parent loader which defined the class A

The last point implies that even if you modify your custom class loader to look up its own classes first instead of following the standard model of querying the parent first, it doesn’t solve the issue if it has no own A, as then, it still returns the parent’s A whose references will be resolved using the parent. Since you are invoking defineClass before asking for A, the lookup order doesn’t matter at all, as your custom loader has an already defined B that it returned if anyone ever asked it for B

So you could let your custom loader also load and define A. Or you use Reflection with access override to defineClass on the system ClassLoader before it loads B. The cleanest solution is to implement the class modification logic as a Java Agent which can use the Instrumentation API to intercept and change the definition of B right at its loading time.

like image 37
Holger Avatar answered Sep 28 '22 01:09

Holger