关于JFinal中Jetty的类加载器问题

1、问题描述

首先是我遇到的问题现象是:前文1 前文2 前文3 前文4

经过一个星期得顺藤摸瓜,最后找到了问题得根源是,ClassLoader不同的问题。引用别人博文的话:

Java中,一个类用其完全匹配类名(fully qualified class name)作为标识,这里指的完全匹配类名包括包名和类名。但在JVM一个类用其全名和一个加载类ClassLoader的实例作为唯一标识,不同类加载器加载的类将被置于不同的命名空间。

因此,在JFinal中的modelToTableMap和modelToConfigMap,虽然Map中都保存着名字相同的类对象,但是由于使用了不同的ClassLoader,也是无法从Map中get到值,每次都是返回Null。

在我的项目中,使用了ServletContextListener来执行一个定时任务,这里使用的ClassLoader是WebAppClassLoader。而在JFinal初始化配置的时候,却使用了AppClassLoader。

另外就是,我使用了Maven来进行了Jar包的管理。

2、ClassLoader的相关知识

首先,AppClassLoader是属于JVM的ClassLoader体系中的一个,属于系统类加载器,另外两个是ExtClassLoader和Bootstrap Loader。三者之间存在的父类委托机制,就是子类接受类加载的请求时,先向父类进行类进行委托加载,如果父类有,则使用父类的加载器,如果没有,才使用子类自身类加载器。

关于JFinal中Jetty的类加载器问题


  1. Bootstrap ClassLoader,负责加载java基础类,主要是 %JRE_HOME/lib目录下的jar和class

  2. Extension ClassLoader,负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class

  3. App ClassLoader,负责加载当前java应用的classpath中的所有类。

然后再说WebAppClassLoader,它是属于特殊的加载机制,它先试图自己载入类(在ContextPath/WEB-INF/lib和classes中载入类),如果无法载入,再请求父ClassLoader完成。 于WEB APP线程,它的contextClassLoader是WebAppClassLoader 。对于Tomcat Server线程,它的contextClassLoader是CatalinaClassLoader  。

根据以上的知识,大概可以把问题解释清楚了。

JFinal的jar包是通过Maven添加到项目中,虽然是添加到了classpath当中,但是并没有放到WEB-INF/lib目录下面。因此,在使用ServletContextListener启动的定时任务时,使用的类加载器是WebAppClassLoader。JFinal进行初始化时,默认也是优先从WebAppClassLoader的目录下寻找相应的类文件,但是由于lib下没有,因此转而向AppClassLoader发出请求,这时就遵循父类委托机制。最后,在ClassPath中找到,也就是Maven中,所以就使用了AppClassLoader。

3、问题

方法一:把定时任务,也放到JFinal中去启动,而不是使用ServletContext,这样就保证了定时任务和JFinal的对象都是在同一个ClassLoader中,就是AppClassLoader。

方法二:把JFinal-1.9.jar包放到WEB-INF/lib目录下,这样就能够保证两者的对象在同一个ClassLoader中,这时就是WebAppClassLoader。

其实,这也是在开发调试中才会遇到的问题,如果打包成war包,放到Tomcat中运行的话,也是不会出现这个问题的。因为,所有使用的jar包都会导出到WEB-INF/lib目录下。

以上就是我个人在该问题中的经验总结,希望能够帮助到大家,谢谢~



你可能感兴趣的:(关于JFinal中Jetty的类加载器问题)