和Java类路径(classpath)打交道的过程中,开发者偶尔会遇到麻烦。这是因为,类装载器实际装入的是哪一个类有时并不显而易见,当应用程序的classpath包含大量的类和目录时,情况尤其严重。本文将提供一个工具,它能够显示出被装入类文件的绝对路径名。

一、Classpath基础

Java虚拟机(JVM)借助类装载器装入应用程序使用的类,具体装入哪些类根据当时的需要决定。CLASSPATH环境变量告诉类装载器到哪里去寻找第三方提供的类和用户定义的类。另外,你也可以使用JVM命令行参数-classpath分别为应用程序指定类路径,在-classpath中指定的类路径覆盖CLASSPATH环境变量中指定的值。

类路径中的内容可以是:文件的目录(包含不在包里面的类),包的根目录(包含已打包的类),包含类的档案文件(比如.zip文件或者.jar文件)。在Unix家族的系统上,类路径的各个项目由冒号分隔,在MS Windows系统上,它们由分号分隔。

类装载器以委托层次的形式组织,每一个类装载器有一个父类装载器。当一个类装载器被要求装载某个类时,它在尝试自己寻找类之前会把请求先委托给它的父类装载器。系统类装载器,即由安装在系统上的JDK或JRE提供的默认类装载器,通过CLASSPATH环境变量或者-classpath这个JVM命令行参数装入第三方提供的类或者用户定义的类。系统类装载器委托扩展类装载器装入使用Java Extension机制的类。扩展类装载器委托自举类装载器(bootstrap class loader)装入核心JDK类。

你可以自己开发特殊的类装载器,定制JVM如何动态地装入类。例如,大多数Servlet引擎使用定制的类装载器,动态地装入那些在classpath指定的目录内发生变化的类。

必须特别注意的是(也是令人吃惊的是),类装载器装入类的次序就是类在classpath中出现的次序。类装载器从classpath的第一项开始,依次检查每一个设定的目录和压缩文件,尝试找出待装入的类文件。当类装载器第一次找到具有指定名字的类时,它就把该类装入,classpath中所有余下的项目都被忽略。

看起来很简单,对吧?

二、可能出现的问题

不管他们是否愿意承认,初学者和富有经验的Java开发者都一样,他们都曾经在某些时候(通常是在那些最糟糕的情形下)被冗长、复杂的classpath欺骗。应用程序所依赖的第三方类和用户定义类的数量逐渐增长,classpath也逐渐成了一个堆积所有可能的目录和档案文件名的地方。此时,类装载器首先装载的究竟是哪一个类也就不再显而易见。如果classpath中包含重复的类入口,这个问题尤其突出。前面已经提到,类装载器总是装载第一个它在classpath中找到的具有合适名字的类,从实际效果看,它“隐藏”了其他具有合适名字但在classpath中优先级较低的类。

如果不小心,你很容易掉进这个classpath的陷阱。当你结束了一天漫长的工作,最后为了让应用程序使用最好、最新的类,你把一个目录加入到了classpath,但与此同时,你却忘记了:在classpath的另一个具有更高优先级的目录下,存放着该类的另一个版本!

三、一个简单的classpath工具

优先级问题是扁平路径声明方法与生俱来固有的问题,但它不是只有Java的classpath才有的问题。要解决这个问题,你只需站到富有传奇色彩的软件巨构的肩膀上:Unix操作系统有一个which命令,在命令参数中指定一个名字,which就会显示出当这个名字作为命令执行时执行文件的路径名。实际上,which命令是分析PATH变量,然后找出命令第一次出现的位置。对于Java的类路径管理来说,这应该也是一个好工具。在它的启发之下,我着手设计了一个Java工具JWhich。这个工具要求指定一个Java类的名字,然后根据classpath的指引,找出类装载器即将装载的类所在位置的绝对路径。

下面是一个JWhich的使用实例。它显示出当Java类装载器装载com.clarkware.ejb.ShoppingCartBean类时,该类第一次出现位置的绝对路径名,查找结果显示该类在某个目录下:

> java JWhich com.clarkware.ejb.ShoppingCartBean

Class 'com.clarkware.ejb.ShoppingCartBean' found in

'/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class'

下面是第二个JWhich的使用实例。它显示出当Java类装载器装载javax.servlet.http.HttpServlet类时,该类第一次出现位置的绝对路径名,查找结果显示该类在某个档案文件中:

> java JWhich javax.servlet.http.HttpServlet

Class 'javax.servlet.http.HttpServlet' found in

'file:/home/mclark/lib/servlet.jar!/javax/servlet/http/HttpServlet.class'

四、JWhich的工作过程

要精确地测定classpath中哪一个类先被装载,你必须深入到类装载器的思考方法。事实上,具体实现的时候并没有听起来这么复杂——你只需直接询问类装载器就可以了!

1: public class JWhich {

2:

3: /**

4: * 根据当前的classpath设置,

5: * 显示出包含指定类的类文件所在

6: * 位置的绝对路径

7: *

8: * @param className

9: */

10: public static void which(String className) {

11:

12: if (!className.startsWith("/")) {

13: className = "/" + className;

14: }

15: className = className.replace('.', '/');

16: className = className + ".class";

17:

18: java.net.URL classUrl =

19: new JWhich().getClass().getResource(className);

20:

21: if (classUrl != null) {

22: System.out.println(" Class '" + className +

23: "' found in '" + classUrl.getFile() + "'");

24: } else {

25: System.out.println(" Class '" + className +

26: "' not found in '" +

27: System.getProperty("java.class.path") + "'");

28: }

29: }

30:

31: public static void main(String args[]) {

32: if (args.length > 0) {

33: JWhich.which(args[0]);

34: } else {

35: System.err.println("Usage: java JWhich ");

36: }

37: }

38: }

首先,你必须稍微调整一下类的名字以便类装载器能够接受(12-16行)。在类的名字前面加上一个“/”表示要求类装载器对classpath中的类名字进行逐字精确匹配,而不是尝试隐含地加上调用类的包名字前缀。把所有“.”转换为“/”的目的是,按照类装载器的要求,把类名字格式化成一个合法的URL资源名。

接下来,程序向类装载器查询资源,这个资源的名字必须和经过适当格式化的类名字匹配(18-19行)。每一个Class对象维护着一个对装载它的ClassLoader对象的引用,所以这里是向装载JWhich类的类装载器查询。Class.getResource()方法实际上委托装入该类的类装载器,返回一个用于读取类文件资源的URL;或者,当指定的类名字不能在当前的classpath中找到时,Class.getResource()方法返回null。

最后,如果当前的classpath中能够找到指定的类,则程序显示包含该类的类文件所在位置的绝对路径名(21-24行)。作为一种调试辅助手段,如果当前classpath中不能找到指定的类,则程序获取java.class.path系统属性并显示当前的classpath(24-28行)。

很容易想象,在使用Servlet引擎classpath的Java Servlet中,或者在使用EJB服务器classpath的EJB组件中,上面这段简单的代码是如何运作。例如,如果JWhich类是由Servlet引擎的定制类装载器装入,那么程序将用Servlet引擎的类装载器去寻找指定的类。如果Servlet引擎的类装载器不能找到类文件,它将委托它的父类装载器。一般地,当JWhich被某个类装载器装入时,它能够找出当前类装载器以及所有其父类装载器所装入的所有类。

【结束语】

如果需要是所有发明之母,那么帮助我们管理Java类路径的工具可以说迟到了很长时间。Java新闻组和邮件列表中充塞着许多有关classpath的问题,现在JWhich为我们提供了一个简单却强大的工具,帮助我们在任何环境中彻底玩转Java类路径。

java .classpath配置_轻松玩转Java配置的Classpath相关推荐

  1. snmp与java集成_轻松地与Java完全集成

    snmp与java集成 这里是如何不使用SQL,HQL,PHP,ASP,HTML,CSS或Javascript而是使用Vaadin的UI层和Speedment Stream ORM完全依赖Java编写 ...

  2. vim java开发环境配置_搭建vim作为java开发环境(-)

    说明:本文的内容作为自己搭建环境的记录,内容基本是从网上查找来的,所以原作者看了不要太介意.另外本人是Mac电脑,请参照者注意了. javacomplete:是在vim开发中提供提示效果的插件. 1. ...

  3. java spark 环境_在 IntelliJ IDEA 中配置 Spark(Java API) 运行环境

    1. 新建Maven项目 初始Maven项目完成后,初始的配置(pom.xml)如下: 2. 配置Maven 向项目里新建Spark Core库 xmlns:xsi="http://www. ...

  4. java python算法_用Python,Java和C ++示例解释的排序算法

    java python算法 什么是排序算法? (What is a Sorting Algorithm?) Sorting algorithms are a set of instructions t ...

  5. 我的世java途径错误_我的世界JAVA路径错误的解决方法分享

    我的世界JAVA路径错误该怎么解决呢?JAVA是Minecraft运行的虚拟环境必要条件,需要您安装后才开始游戏.很多童鞋在安装过程中都遇到了JAVA路径错误的提示,这是什么原因导致的?第一手游网必须 ...

  6. java future用法_纯干货:Java学习过程中的21个知识点和技术点

    我们在Java学习过程中要学会抓重点,善于总结,Java学习过程中常见的21个知识点和技术点你知道吗?下面和千锋广州小编一起来看看吧! 1. JVM相关 对于刚刚接触Java的人来说,JVM相关的知识 ...

  7. delphi7aes加密解密与java互转_跨语言(java vs python vs nodejs)的RSA加解密问题探讨

    多次被问到这样的问题: java服务端的rsa加密操作已经完成,返回一个16进制的字符串给python平台,但是在python进行私钥解密的时候发现行不通.... 前端python加密,后端用java ...

  8. java 程序命令_命令行运行JAVA程序

    如果出现找不到变量,核查环境变量是否配置好 可以运行javac java java -version看是否正常 注意要在对应的目录下 核查java文件里边的名字 class文件名是否与java文件名一 ...

  9. java笔试题_一道简单的 Java 笔试题,但值得很多人反思

    专注于Java领域优质技术,欢迎关注 作者:匿蟒 前言 面试别人,对我来说是一件新奇事,以前都是别人面试我.我清楚地知道,我在的地域与公司,难以吸引到中国的一流软件人才.所以,我特地调低了期望,很少问 ...

最新文章

  1. webfocus在移动终端上的应用DEMO
  2. WPF 与Surface 2.0 SDK 亲密接触 - 图形缩放篇
  3. Mysql Explain的简单使用
  4. (转)C# 根据当前时间获取,本周,本月,本季度等时间段 .Net中Exception
  5. Scala---Import子句详解
  6. 【模块】【通信】---http模块中req和res 常用的属性介绍
  7. 大数据 数据平台方案评估
  8. Oracle:grouping和rollup
  9. atitit.信息安全的控制总结o7
  10. 基于C# 的 WinForm 开发 (一、C# 快速入门)
  11. EasyPusher手机直播推送是如何实现后台直播推送的
  12. 怎么快捷制作网站icon图标工具
  13. 哪款骨传导耳机好用,好用的骨传导耳机汇总
  14. 【翻译】--19C Oracle 安装指导
  15. 使用css实现一个三角形
  16. 青藤《关键信息基础设施增强保护安全实践》论文入选中国科技核心期刊
  17. 解决VC6.0出现的Error spawning cl.exe错误
  18. 给Date日期加上时分秒
  19. 树莓派通过java获取麦克风声音并播放
  20. 声发射能量和事件参数的Excel VBA处理1/2

热门文章

  1. spring mvc 接入cas登录
  2. Github上AI在银行和保险的应用列表
  3. 201621123069 《Java程序设计》第十一周学习总结
  4. UE如何使用正则表达式
  5. uploadify 上传
  6. cacti被监控机上snmp的安装及报错解决
  7. 修改docker默认网桥
  8. 数据库收缩数据文件的尝试(二)(r11笔记第9天)
  9. RabbitMQ学习系列(五): RPC 远程过程调用
  10. SQL SERVER 2008 利用发布订阅方式实现数据库同步