51 lines
2.7 KiB
Plaintext
51 lines
2.7 KiB
Plaintext
https://kostenko.org/blog/2019/06/runtime-class-loading.html
|
|
|
|
How to use custom ClassLoader to load jars in runtime
|
|
28 June 2019
|
|
|
|
To load calsses in runtime java uses ClassLoader mechanism which is based on next core principles:
|
|
|
|
delegation - by default uses parent-first delegation, - child ClassLoader will be used if parent is not able to find or load class. This behavior can be changed to child-first by overwriting ClassLoader.loadClass(...);
|
|
visibility - child ClassLoader is able to see all the classes loaded by parent but vice-versa is not true;
|
|
uniqueness - allows to load a class exactly once, which is basically achieved by delegation and ensures that child ClassLoader doesn't reload the class already loaded by parent;
|
|
The main scenarios to use custom ClassLoader is:
|
|
|
|
Class Instrumentation - modifying classes at runtime. For example, to unit testing, debugging or monitoring;
|
|
Isolation of executions - isolate several execution environments within a single process by making visible only a subset of classes for a particular thread, like it does in EE environments;
|
|
So, let's see how using of custom ClassLoader looks from source code perspective:
|
|
|
|
List<File> jars = Arrays.asList(new File("/tmp/jars").listFiles());
|
|
URL[] urls = new URL[files.size()];
|
|
for (int i = 0; i < jars.size(); i++) {
|
|
try {
|
|
urls[i] = jars.get(i).toURI().toURL();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
URLClassLoader childClassLoader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
|
|
Then load class with custom ClassLoader:
|
|
|
|
Class.forName("org.kostenko.examples.core.classloader.ClassLoaderTest", true , childClassLoader);
|
|
Note! If your loaded libraries uses some resources like properties or something else, you need to provide context class loader:
|
|
|
|
Thread.currentThread().setContextClassLoader(childClassLoader);
|
|
Also, you can use custom ClassLoaders to load services with Java Service Provider Interface(SPI)
|
|
|
|
ServiceLoader<MyProvider> serviceLoader = ServiceLoader.load(MyProvider.class, childClassLoader);
|
|
...
|
|
|
|
---
|
|
https://stackoverflow.com/questions/60764/how-to-load-jar-files-dynamically-at-runtime
|
|
|
|
The reason it's hard is security. Classloaders are meant to be immutable; you shouldn't be able to willy-nilly add classes to it at runtime. I'm actually very surprised that works with the system classloader. Here's how you do it making your own child classloader:
|
|
|
|
URLClassLoader child = new URLClassLoader(
|
|
new URL[] {myJar.toURI().toURL()},
|
|
this.getClass().getClassLoader()
|
|
);
|
|
Class classToLoad = Class.forName("com.MyClass", true, child);
|
|
Method method = classToLoad.getDeclaredMethod("myMethod");
|
|
Object instance = classToLoad.newInstance();
|
|
Object result = method.invoke(instance);
|
|
Painful, but there it is. |