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.
Load jar dynamically at runtime?
(Applies to Java version 8 and earlier).
Indeed this is occasionally necessary. This is how I do this in production. It uses reflection to circumvent the encapsulation of addURL
in the system class loader.
/*
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws MyException
{
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new MyException(e);
}
}
How to load a jar file at runtime
Reloading existing classes with existing data is likely to break things.
You can load new code into new class loaders relatively easily:
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { yourURL },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();
Class loaders no longer used can be garbage collected (unless there is a memory leak, as is often the case with using ThreadLocal, JDBC drivers, java.beans
, etc).
If you want to keep the object data, then I suggest a persistence mechanism such as Serialisation, or whatever you are used to.
Of course debugging systems can do fancier things, but are more hacky and less reliable.
It is possible to add new classes into a class loader. For instance, using URLClassLoader.addURL
. However, if a class fails to load (because, say, you haven't added it), then it will never load in that class loader instance.
Java 9 - add jar dynamically at runtime
From the documentation of the Class.forName(String name, boolean initialize, ClassLoader loader)
:-
throws
ClassNotFoundException
- if the class cannot be located by the specified class loader
Also, note the arguments used for the API includes the name of the class using which the classloader returns the object of the class.
Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface.
In your sample code, this can be redressed to something like :
// Constructing a URL form the path to JAR
URL u = new URL("file:/C:/Users/SomeUser/Projects/MyTool/plugins/myNodes/myOwn-nodes-1.6.jar");
// Creating an instance of URLClassloader using the above URL and parent classloader
ClassLoader loader = URLClassLoader.newInstance(new URL[]{u}, MyClass.class.getClassLoader());
// Returns the class object
Class<?> yourMainClass = Class.forName("MainClassOfJar", true, loader);
where MainClassOfJar
in the above code shall be replaced by the main class of the JAR myOwn-nodes-1.6.jar.
Dynamically load a jar
Have a look at the seminal paper "Dynamic class loading in the Java virtual machine", by Bracha and Liang. They explain how class loaders work, and how you can use interfaces and class loader to support dynamic changes. It's a worthy read (you should be able to obtain a free PDF by googling the name of the paper) !
Related Topics
What Is the Meaning of "This" in Java
How to Concatenate Two Arrays in Java
How to Remove Repeated Elements from Arraylist
Difference Between a Variable, Object, and Reference
How to Handle Infinite Loop Caused by Invalid Input (Inputmismatchexception) Using Scanner
How Does Java Handle Integer Underflows and Overflows and How Would You Check For It
What Is Double Brace Initialization in Java
"Comparison Method Violates Its General Contract!"
What Is the Java String Pool and How Is "S" Different from New String("S")
How to Read Input from the Console Using the Scanner Class in Java
Design Patterns Web Based Applications
Unsupported Major.Minor Version 52.0
Java.Lang.Classnotfoundexception: Didn't Find Class on Path: Dexpathlist