本文共 2767 字,大约阅读时间需要 9 分钟。
转
自定义ClassLoader相信很多人都用过,网上文章也有很多。但如何使用自定义的ClassLoader有时确实比较头痛。
如果启动入口自己可以控制还好说,大不了通过自定义ClassLoader加载所有类就可以了,但如果控制不了,比如说是通过TOMCAT或脚本来启动的,但又要用自定义的ClassLoader来加载外部类,那就郁闷了。
我碰到的情形就是如此,其中的ClassLoaderC是tomcat的类加载器,而classLoaderD是自定义的类加载器。通常来说,我们只能选择访问C或D其中一个下面的类。有没办法能同时访问它们下两个的类呢?
其中一种办法是Thread.currentThread().setContextClassLoader。相对比较方便,但这在多线程环境下很容易产生问题。
还有一种办法是通过反射调用,修改ClassLoaderC的parent为ClassLoaderD。我们知道ClassLoader的委托机制是先让parent(父)类加载器寻找,只有在parent找不到的时候才从自己的类路径中去寻找。这样我们通过修改parent就能达到同时访问的目的。当然,由于parent是私有的,而且没有提供写方法,所以还需要用反射来设置。
之前还尝试了另一种方法,即ClassLoader.addClass,但发现类是进去了,但package里没有,还是会加载不到类。
这种方法目前还在试用,大家觉得有什么问题欢迎提出来:)
public class ContainerClassLoader extends ClassLoader { private Map> loadedClasses = new HashMap >(); private static ContainerClassLoader INSTANCE; private ContainerClassLoader() { super(ContainerClassLoader.class.getClassLoader().getParent()); } /** * 初始化 */ public static void init() { INSTANCE = new ContainerClassLoader(); try { INSTANCE.addThisToParentClassLoader(ContainerClassLoader.class .getClassLoader()); } catch (Exception e) { System.err.println("设置classloader到容器中时出现错误!"); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (loadedClasses.containsKey(name)) { return loadedClasses.get(name); } return super.loadClass(name, resolve); } /** * 将this替换为指定classLoader的parent ClassLoader * * @param classLoader */ private void addThisToParentClassLoader(ClassLoader classLoader) throws Exception { Field field = ClassLoader.class.getDeclaredField("parent"); field.setAccessible(true); field.set(classLoader, this); } }
另外,在过程中还碰到spring加载时,classloader还没修改的问题。后来通过在web.xml中增加listener-class来实现。
后面还碰到了问题,如果将TOMCAT的当前ClassLoader的parent修改了,还是会碰到类无法识别的问题。
原因是WebappClassLoader类也有个私有属性,与基类ClassLoader名称一样,而且是在WebappClassLoader类构造时赋值。 处理方法:通过反射将WebappClassLoader对象的两个私有属性都修改。 如下:private void addThisToParentClassLoader(ClassLoader classLoader) throws Exception { Field field; try { //将当前ClassLoader的parent属性修改为本对象(适用于WebClassLoader) field = classLoader.getClass().getDeclaredField("parent"); field.setAccessible(true); field.set(classLoader, this); } catch (Exception e) { } //将当前ClassLoader的parent ClassLoader修改为本对象 field = ClassLoader.class.getDeclaredField("parent"); field.setAccessible(true); field.set(classLoader, this); }
转载地址:http://sndoi.baihongyu.com/