java动态加载指定的类或者jar包反射调用其方法-涉及其他jar中的类就报ClassNotFound问题分析及解决思路...

在这篇博文中java动态加载指定的类或者jar包反射调用其方法,介绍动态加载指定的类,当时我是拿URLClassLoader介绍的,当然可以自定义一个ClassLoader重写对应的方法, 不过这个有现成的比自己重写更强大为何不用。

话不多说,很久不写博客了,时间太紧了,起因还是一个网友私信我,说他反射创建某个对象时涉及其他jar中的类就会报ClassNotFound异常。

其实,我也遇到过这个问题,在项目中反射创建一个service也是这样的,涉及到第三方的jar可能就出这问题,当时我可是纠结了好久,花了好几天时间去解决,请教过诸多大神和网友,不过还是不行,问题沉溺了有一阵子。

网上很少又提到这个问题的。最近又请教几个有经验的网友说是没有findClass的原因,或是类名或者service的原因,应该是我描述问题没描述清楚吧。而且关键这个jar呀你不反射创建项目中用的好好的,就和昨天晚上私信我的那个网友一样,按JVM的双亲委托模型不应该啊,不熟悉的可以看看这译文个深入分析Java ClassLoader原理,自定义或者URLClassLoader的父加载器是App ClassLoader,而这个jar或者类肯定它会找到并加载或者提前就加载到JVM里呢,怎么回事呢。

其实你如果这样想,我们都犯了一个严重的错误,就是我们不是单纯依托与JVM,程序结束就OK了,可能你测试的时候没问题,已到项目就有这问题。问题在于我们往往是运行在个应用服务器下,一般都是tomcat吧,而tomcat有自己的一套加载机制,有自己一些加载器,我们忽略这个因素才是导致了问题发生的根本原因。

我们忽略了tomcat内部自定义的类加载器只想到了JVM的那几个加载器,tomcat有个叫webApp的加载器它是先加载WEB-INF/classes后在加载WEB-INF/lib,但它的父加载器是它的common加载器,comon的父加载器是system加载器(和JVM的应用程序加载器功能差不多,不过指定了其他tomcat目录下的加载,大家可以看看官网上的英文文档),但是源码中这个加载器是URLClassLoader的子类,而URLClassLoader默认父加载tomcat下是它的system加载器这么设计和tomcat的配置有关,默认为无为false,会直接委托给tomcat的system加载器加载system委托最顶层的Bootstrap加载器(差不多是JVM里起始加载器和扩展加载器的合并),但不管怎么样,项目在tomcat下自定义的或者URLClassLoader加载默认父加载器都不会是tomcat的webApp加载器而是system加载器,或者自定义的加载器或URLClassLoader和tomcat的webApp加载器没有上下关系,所以动态创建类时设计到其他类时肯定会报CNF异常。

解决思路就是先获取当前类的Class,然后获取当前类的加载器,在自定义的加载器或者URLClassLoader加载器创建时指定为它们的父加载器,这样问题就会游刃而解了,可能平常我们测试写个简单的例子没遇到这个问题,因为我们那时的URLClassLoader或者自定义的加载器的父加载器都是JVM的第三次加载器即应用程序加载,它是专门加载classpath下边的或者指定的类或者jar的,依照双亲委托模型,肯定会找到引入路径的那个类或者jar的。

或者我们使用Class.forName()的方式来动态加载指定的类,就不会存在这个问题,因为这种方式一方面是能初始化类的静态东西,再就是重要一点,就是采用的加载当前所在类的加载器来加载你指定的类,这样你在tomcat下那就是它的webApp加载器啊,肯定不再出现这个问题,可能直接就从缓存里找到了。

参考:《深入理解java虚拟机:JVM高级特性与最佳实战第二版》和apache官网文档http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html,以及tomcat7源码。

声明:以上陈述都是基于JDK7,tomcat7。

时间仓促,可能有不妥后者不严谨的地方,忘留言指正,十分感激!

你可能感兴趣的:(java动态加载指定的类或者jar包反射调用其方法-涉及其他jar中的类就报ClassNotFound问题分析及解决思路...)