tomcat下使用自定义类加载器遇到的问题

http://freewind886.blog.163.com/blog/static/6619246420126141074308/

上周有用户在试用产品里一个与自定义classloader相关的功能时,发现在Junit下运行正常,部署到tomcat运行就会报错。
功能概述如下:
用户根据需要在可视界面上编写一个接口Hash的实现类,然后源码经过动态编译(借助jdk中的tools.jar包)在临时文件夹中生成class文件,读取该文件进入内存就可以存到数据库或者通过网络发送到其他Java进程。其他进程获取到class文件的byte[]后,通过一个自定义的classloader就可以创建出类实例。
现在的问题就在于这个自定义的classloader在Junit下可以加载byte[]创建类实例,而在tomcat下则抛出加载错误。之前曾了解过tomcat下的类加载机制和常见的Java应用不一样,所以开始的时候估计问题应该是出在这个地方,后来同事在断点调试下也明确了问题的原因。
推荐看这篇文章,里面详细介绍了Java的类加载机制:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 

借用里面的一张图:

tomcat下使用自定义类加载器遇到的问题 - 小柯 - 点点足迹
  
对于直接通过java命令启动的Java应用,classpath里的类都由系统类加载器加载到进程中。我们产品中的自定义类加载器MyClassLoader在加载class文件的byte[]时,会检测到该类引入了接口Hash,MyClassLoader会将Hash的加载代理给父加载器(系统类加载器),系统类加载器在classpath中找到Hash的定义然后加载,接着整个加载操作就可以顺利的完成。
在tomcat环境下,类加载的机制就不同了,具体可见 http://blog.163.com/haizai219@126/blog/static/44412555200810111429791/
借用一图如下:
               Bootstrap
                   |
                System
                   |
                Common
               /       \
        Catalina      Shared
                             /... ...\
                webapp1     webappN 
在这种架构下,应用的类都由webappX类加载器来加载,而自定义的类加载器MyClassLoader的父加载器也就应该是webappX,那在加载网络传送过来的类定义的时候,也不会出现问题。最后在定位问题的时候,就是发现MyClassLoader的父加载器不是webappX,而是System,即上面提到的系统类加载器。
MyClassLoader(内部类)的实现如下:

 privatestaticclassMyClassLoaderextendsClassLoader{
     privatebyte[] classData =null;
     privateMyClassLoader(){
          super();
     }
     protectedClass<?> findClass(String name){
          return defineClass(name,this.classData,0,this.classData.length);
     }
     privateObject newInstance(String name)throwsInstantiationException,
               IllegalAccessException,ClassNotFoundException{
          return loadClass(name,true).newInstance();
     }
}

  
查看ClassLoader的无参构造函数,可以看到一行代码
this.parent = getSystemClassLoader();
这就是说,如果自定义的classloader如果不指定父加载器,则会被默认设置为系统类加载器。这就是问题的所在。
当MyClassLoader在加载Hash的实现类定义时,让父加载器(系统类加载器)去加载Hash接口定义。在tomcat的类加载机制下,系统类加载器实际上并不能加载应用功能相关的类,这就导致加载Hash失败。
修改的方法就是在创建MyClassLoader的时候,传入合适的父加载器。可以找一个使用了MyClassLoader的类,通过Class.getClassLoader()获取到所应该依附的父加载器。这样在tomcat或者普通的Java应用都可以解决加载出错的问题。

你可能感兴趣的:(tomcat)