问题
开发过程中,咱们经常需要使用不同的库版本,而这些版本又不是向后兼容的,或者出于某种原因需要支持同一库的多个版本。
在这种情况下,默认的类加载器已经是不支持了,因为
loadClass
方法只加载一次特定的类,之后所有的加载请求就直接返回现有 Class 实例的引用了。
解决办法
在这种情况下,可以自定义一个类加载器,用这个具有优先设置的加载器加载需要的库就可以了。
基本代码如下:
1 import java.net.URL;
2 import java.net.URLClassLoader;
3 import java.util.List;
4
5 public class CustomClassLoader extends ClassLoader {
6 private ChildClassLoader childClassLoader;
7
8 public CustomClassLoader(List<URL> classpath) {
9
10 super(Thread.currentThread().getContextClassLoader());
11 URL[] urls = classpath.toArray(new URL[classpath.size()]);
12 childClassLoader = new ChildClassLoader(urls, new DetectClass(this.getParent()));
13 }
14
15 @Override
16 protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
17 try {
18 return childClassLoader.findClass(name);
19 } catch(ClassNotFoundException e) {
20 return super.loadClass(name, resolve);
21 }
22 }
23
24 private static class ChildClassLoader extends URLClassLoader {
25 private DetectClass realParent;
26
27 public ChildClassLoader(URL[] urls, DetectClass realParent) {
28 super(urls, null);
29 this.realParent = realParent;
30 }
31
32 @Override
33 public Class<?> findClass(String name) throws ClassNotFoundException {
34 try {
35 Class<?> loaded = super.findLoadedClass(name);
36 if(loaded != null) return loaded;
37 return super.findClass(name);
38 } catch(ClassNotFoundException e) {
39 return realParent.loadClass(name);
40 }
41 }
42 }
43
44 private static class DetectClass extends ClassLoader {
45 public DetectClass(ClassLoader parent) {
46 super(parent);
47 }
48
49 @Override
50 public Class<?> findClass(String name) throws ClassNotFoundException {
51 return super.findClass(name);
52 }
53 }
54 }
All Done!
至此就算完成了,通过这种解决办法就可以使单个 jvm 中有多个库版本,或者同一个版本中由于静态变量导致的多实例共存了。
注意:有些库中可能还加载了一些 classpath 下的资源文件,这种情况下就按需要覆盖 getResource 等方法就可以了。
极限就是为了超越而存在的