I have stumbled over this issue a while ago. As many, I had used a method similar to that in the question
private static int AddtoBuildPath(File f)
to dynamically add paths to the classpath at runtime. The code in the question is probably bad style in multiple aspects: 1) assuming that ClassLoader.getSystemClassLoader()
returns an URLClassLoader
is an undocumented implementation detail and 2) using reflection to make addURL
public is maybe another one.
Cleaner way to dynamically add classpaths
In case that you need to use the additional classpath URLs for class loading through „Class.forName
“, a clean, elegant and compatible (Java 8 to 10) solution is the following:
1) Write your own class loader by extending URL classloader, having a public addURL
method
public class MyClassloader extends URLClassLoader {
public MyClassloader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
2) Declare a (singleton/app wide) object of your classloader
private final MyClassloader classLoader;
and instanciate it via
classLoader = new MyClassloader(new URL[0], this.getClass().getClassLoader());
Note: The system class loader is the parent. Classes loaded though classLoader
know those who can be loaded through this.getClass().getClassLoader()
but not the other way around.
3) Add additional classpaths whenever needed (dynamically):
File file = new File(path);
if(file.exists()) {
URL url = file.toURI().toURL();
classLoader.addURL(url);
}
4) Instanciate objects or your app though your singleton classloader via
cls = Class.forName(name, true, classLoader);
Note: Since class loaders try a delegation to the parent class loader prior loading a class (and the parent to its parent), you have to make sure that the class to load is not visible to the parent class loader to make sure that it is loaded through the given class loader. To make this clearer: if you have ClassPathB
on your system class path and later add ClassPathB
and some ClassPathA
to your custom classLoader
, then classes under ClassPathB
will be loaded through the system classloader and classes under ClassPathA
are not known to them. However, if you remove ClassPathB
from you system class path, such classes will be loaded through your custom classLoader
, and then classes under ClassPathA are known to those under ClassPathB.
5) You may consider passing your class loader to a thread via
setContextClassLoader(classLoader)
in case that thread uses getContextClassLoader
.