最近做一个web项目,服务器用的是tomcat,需要一个动态添加jar库的功能(系统不停机),我在程序中用URLClassLoader动态加载新的jar,发现无法引用项目里面WEB-INF/lib下的jar库,老是报ClassNotFoundException,我在没有动态加载jar之前看了看原有jar中类的加载器是org.apache.catalina.loader.WebappClassLoader:Class<?>c = Class.forName("org.slf4j.Logger");//slf4j的jar包在WEB-INF/lib目录下
out.print(c.getClassLoader().getClass().getName());
输出结果是WebappClassLoader,这个类是java.net.URLClassLoader的子类,属于最低优先级的类加载器,现在我用URLClassLoader加载新的jar文件,就出错:String className;//类名
URL[] url;//url数组
//省略变量初始化,保证变量值正确
ClassLoader loader = new URLClassLoader(url);
Class<?> c = loader.loadClass(className);//这里抛ClassNotFoundException异常
Object obj = c.newInstance();不过这里抛出的异常不是className没找到,而是className的父接口没找到,className父接口所在的jar包在WEB-INF/lib目录下。我尝试了下面两个办法解决:
1.用Java Instrumentation把className父接口所在的jar包加入系统classpath中,这时loadClass正常了,但是后面无法直接调用类的方法了,必须全部用反射调用,这样需要修改大量代码,就没有继续。
2.把WEB-INF/lib里面的jar全部拷到%JRE_HOME%\lib\ext里面,现在一切都正常了,但是这样需要修改系统配置,而我更希望能在程序代码中解决这个问题,或者最多是修改项目配置。
不知道大家还有没有其它解决方法,我希望能通过在ClassLoader下手解决。谢谢!

解决方案 »

  1.   

    你可以试着自己写一个ClassLoader,不过这个方式解决不了文件Class的版本问题,因为虚拟机的这个类空间加载了这个类,在其回收之前,会一直用内存中这个类,而不会去文件中加载。至于哪个类装载其先装载,这个并不用担心,按照类加载器的双亲委派模型,如果优先级类装载其未能加载类,他会委派给其子类装载器装载,如果出现ClassNotFountException,则说明你没有任何一个类装载其能够装载这个类,或者说能够找到这个类并装载,这不是一个优先级的问题。
      

  2.   

    感谢你的回答,我明白为什么无法用Instrumentation加载了,对于web项目里面的Java类文件,它们的加载器是org.apache.catalina.loader.WebappClassLoader,我以此调用getParent()方法,得到的父加载器为:
    org.apache.catalina.loader.StandardClassLoader
    sun.misc.Launcher$AppClassLoader
    sun.misc.Launcher$ExtClassLoader
    而URLClassLoader的父加载器为:
    sun.misc.Launcher$AppClassLoader
    因此URLClassLoader在本身无法加载的情况下,也无法请求父加载器加载类,就抛出ClassNotFountException了。
    而%JRE_HOME%\lib\ext下的类是由sun.misc.Launcher$ExtClassLoader加载的,这也是为什么把jar复制到%JRE_HOME%\lib\ext可以正常加载的原因。
      

  3.   

    优先级:启动类加载器,扩展类加载器,classpath类加载器,自定义类加载器
    java的安全模式下,采用双亲委派模式加载一个类,当某个对象需要加载一个类的时候,首先请求他的双亲,双亲继续向它的双亲请求...直到根——启动加载器。
    如果启动加载器能提供这个类,那么就返回这个类。如果不能,就让请求启动加载器的子加载这个类
    如果都不能加载这个类,就用这个对象的自定义类加载器加载这个类。
      

  4.   

    我关注这个问题。 这个问题我也遇到过。 自己想做 动态加载程序。
      但是加载过程中我用的 URLClassLoader 用这个加载的话,我这里的程序是需要把加载的放在一个文件里面
     声明的时候 要写父路径。 
        我想问的是 你的包更新或者添加。添加的话 能够接受。 怎么调用
     覆盖的话 怎么调用呢?