动态装载问题的研究

news/2024/5/20 7:50:14
 
1         问题背景
我们都知道,Java平台一大亮点就在于其类装载器体系结构,这使得JVM可以在运行期从Java API,扩展路经(java.ext.path),classpath以及用户指定的位置(文件或网络)中载入所需的class,从而达到动态装载的目的。然而其类装载器委托模型在保证了安全性和强大功能的同时,也导致了相当的复杂性,有很多地方一旦我们不加注意的话就将导致错误。这里我希望通过一些小例子来展示动态装载的某些方面,深入地了解一下怎么进行动态装载,会遇到什么样的问题,并就问题的原因与解决方法进行讨论。
也许有人会说:我的程序不用什么动态装载,平时运行程序就是java –classpath … myPkg.my不就行了吗?不过我总是听前辈说,要想完全了解一个系统下的程序设计,就必须要深入研究这个平台的特性,做到心中有数,做起程序才不会处处制肘。类的动态装载是Java平台最显著的特征之一,许多著名的项目—Tomcat,Eclipse都使用自定义类装载器来装载运行时所需的类,如果连什么是动态装载和怎么动态装载都没弄明白,还能去玩自定义类装载器吗?所以还是不要浮躁,让我们从基础开始做起吧。
不过,我要说明的是你在看我这篇文章之前最好先熟悉一下Java的类装载器体系结构以及其委托模型,我在这里将不加赘述,推荐先看《Java深度历险》或《深入Java虚拟机》的第八章。
2         问题研究
我要讨论的问题是:让一个在my/目录下名为my.Main的类在运行期读取util/中的util.Tool类。
l      实验一
我的实验目录结构如下
其中my.Main的代码如下:
util.Tool的代码如下:
在my.Main的代码中16行可以看到,我定义了一个URLClassLoader,想要读取在同一根目录下的/util/Tool.class,接下来我们运行如下命令,来查看运行结果:
如上可见,util.Tool被顺利装载了,可是装载它的却是AppClassLoader,而不是我们想要的URLClassLoader!这是为什么呢?
原因很简单,因为我们将util/Tool.class放到了JVM系统变量”user.dir”所指定的目录下,而该JVM系统变量的默认值为程序所在的根目录,相当于把classpath 设置成了 ”.”,也就是相当于执行了命令:
java –classpath . my.Main
于是,正因为util.Tool在classpath下,于是JVM就在在载入my.Main的同时也用AppClassLoader将util.Tool载入到JVM了。我们可以通过如下命令看得更清楚一些:
java –verbose my.Main
看到了吗?由于AppClassLoader预先载入了util.Tool类,而且在URLClassLoader的loadClass()方法中有如下语句:Class cls = getLoadedClass(String className);如果cls!=null的话,loadClass()就会返回已经载入过的(即由AppClassLoader所载入的)util.Tool类,所以我们才会看到以上结果。
l      实验二
为了解决以上问题,我决定按如下方式组织我的目录结构
然后对Main.class进行一些修改:
嗯,这下我们不会为util.Tool被提前载入而烦恼了,因为载入my.Main的AppClassLoader找不到换了位置的util.Tool类了。这回可能有人会问:subdir不是还在”user.dir”所指向的目录下吗?怎么只是多建了一层目录它就找不到了呢?原因是AppClassLoader是URLClassLoader的子类,它在载入class的时候也是根据该类的URL来进行定位的。那我们的例子来说,在“实验一”的最后一个图中我们可以看到,util.Tool被载入的位置为: file1:c:/Test1,JVM得到这个URL之后,将util.Tool中的”.”变为”/”,在所得字符串结尾处加上”.class”,最后将该字符串加到 file1:c:/Test1后面,就得到了最终util.Tool的URL: file1:c:/Test1/util/Tool.class。而当加了一层subdir目录之后,AppClassLoader就找不到 file1:c:/Test1/util/Tool.class了,而只能由程序中定义的URLClassLoader来载入。
 这时我们运行程序,所得结果如下:
可以看到,util.Tool确实已经我定义的URLClassLoader被载入并初始化了,可是为什么最后却有一个奇怪的异常呢?NoClassDefFoundError?util.Tool类不是都载入了吗?怎么还找不到它的定义呢?
这个问题需要从JVM的装载机制说起。对于每个JVM实例,除了内置的三个内置的类装载器(BootStrapClassLoader,ExtClassLoader,AppClassLoader)之外,还可能有一些用户自定义的类装载器,而这些类装载器可能在一个程序运行周期内载入同一个类,即AppClassLoader和我自定义的类装载器MyClassLoader可能同时需要载入util.Tool类。为了避免冲突,JVM为每个类装载器都建立一个命名空间,并以此作为其访问边界。例如,如果my.Main想要引用util.Tool类,则它们必须是由同一装载器载入的,也就是说,由于my.Main是由AppClassLoader载入的,那么util.Tool也必须由AppClassLoader载入才行,否则就会出现上述错误。
问题解决的方法很简单,只需要让my.Main和util.Tool由同一类装载器载入就行了。
l      实验三
为了让my.Main和util.Tool由同一类装载器载入,我定义了一个启动类—boot.BootStrap类:
在这里,我用自己的类装载器ucl来装载my.Main类,并用reflection API来调用其main()方法,同时也对my.Main稍作修改
然后执行一下看看结果:
图中可以很清楚的看到 BootStrap 类则是由 AppClassLoader 载入的,而 Main 类和 Tool 类都是由同一个 URLClassLoader 在运行期动态载入的。至此,我们完成了对类的动态载入的全部说明和实验,希望对你能有所帮助。 



http://www.niftyadmin.cn/n/3653609.html

相关文章

HTML5 音视频标签的方法、属性和事件

方法 方法描述addTextTrack()为音视频加入一个新的文本轨迹canPlayType()检查指定的音视频格式是否得到支持load()重新加载音视频标签play()播放音视频pause()暂停播放当前的音视频 属性 属性描述audioTracks返回可用的音轨列表(MultipleTrackList对象&#xff09…

One-Jar之旅

1 问题的提出作为一个经常使用Java编程的程序员,当我在发布我的Java程序的时候,我习惯于这样组织所有的程序和资源:主程序放到JVM系统变量“user.dir”所指向的目录中(假设是MyAppDir目录),程序…

JasperReport+iReport高级报表设计实战

JasperReportiReport高级报表设计实战序言一直以来,报表都是很多项目中一个重要的、不可获取的组成部分。然而其复杂性和专业性又使得程序员不能够也没时间自己设计属于目前手头正在构建的系统的报表模块;即便设计来了又可能由于通用性等原因不能够应用到…

获得OpenCms的数据库链接池

看到有网友问“是否可以修改OpenCms的表结构,修改之后如何访问”,答案是“可以”,OpenCms有自己的数据库链接池,在/WEB-INF/config/opencms.properties文件中配置,默认数据库链接池的名称是“default”,可以…

华丽转身—如何从程序员走向技术管理岗位

华丽转身是华而不实的假面具,我作为一名技术管理人员,建议大家不要轻易的转向管理岗位,坚持自己的技术才是根本。因为只有10%的技术专业人士具备相应的管理岗位所需要的特质,而更少的这样的人能够走到最后,管理岗位所做…

OpenCms开发之——自定义结构化类型

OpenCms中很重要的一个特性就是XML内容,通过XML内容,你可以创建自已的结构化内容,如新闻等,下面通过创建一个简单的“测试新闻”类型来介绍一下OpenCms的这一重要特性:1、新建模块“org.opencms.testnews(模…

OpenCms新手上路“一线穿”

有必要把以前的文章穿穿线了,希望同样的文章带给你不同的感觉……1、OpenCms官方网站:    www.opencms.org    www.alkacon.com/alkacon/en  2、了解OpenCms:    OpenCms简介     OpenCms溯源    …