2

I'm trying to force the System java classloader (i.e. ClassLoader.getSystemClassLoader()) to load an external class defined by a byte array with valid bytecode so that other classes subsequently loaded by this classloader can know about and instantiate the external class without getting a NoClassDefFoundError.

This surely does not work as it only defines the class on the classloader created, not in the System classloader:

URLClassLoader child = 
   new URLClassLoader(new URL[] { myJar.toURI().toURL()                          
   , ClassLoader.getSystemClassLoader());
Class.forName ("com.MyClass", true, child);

The code above will define com.MyClass for the child classloader, not for the system classloader.

Any way of accomplishing that?

chicago-ny
  • 29
  • 4

1 Answers1

1

You can use Reflection with access override:

Method define = ClassLoader.class.getDeclaredMethod("defineClass",
                                      String.class, byte[].class, int.class, int.class);
define.setAccessible(true);
Class<?> clazz = (Class<?>)define.invoke(ClassLoader.getSystemClassLoader(),
                                      null, array, 0, array.length);

The access override is needed because we’re invoking a protected method, but being a protected method, it’s still part of the API, which exists in all implementations and won’t go away in future versions.


Java 9 introduced an astonishing simple way to achieve the same without a hack, as long as your own class has been loaded through the application class loader as well (as is the default):

Class<?> clazz = MethodHandles.lookup().defineClass(array);

This simply creates the class within the same class loading context as the class containing this statement.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    Hacking into ClassLoader.defineClass is not a good solution as it calls a protected method from the wrong context. Also it will break when the java.base module is fully encapsulated. Lookup.defineClass is the right API going forward. Your comment about the code loaded via the application class loader isn't quite right, instead the requirement is that you have a Lookup to a class in the target package with PACKAGE access. See also MethodHandles.privateLookupIn. – Alan Bateman Feb 15 '18 at 12:13
  • @AlanBateman I really hope that it’s obvious to the reader, which solution is to prefer, once switched to Java 9 or higher. I didn’t mention the package constraint, still, this doesn’t contradict my statement addressing the OP’s goal. The resulting class will be in the same class loading context (and package) of the class containing this statement (as this statement includes the `MethodHandles.lookup()` expression which nails down the lookup’s context). This answer was not meant to discuss all the possibilities of a lookup object, it’s about solving the OP’s requirement with this tool… – Holger Feb 15 '18 at 12:22