OFBiz 开发指南系列
ApacheOFBizAdvancedFramework
Hongs 收集整理
Contact:billhongs@gmail.com
本人最近基于这套开源 ERP 架构开发了几个电子商务系统,感觉
这个架构功能强大,性能也非常好,所以打算整理出一套开发指南来,
希望更多的人能快速使用这套架构。我打算先把技术方面的先整理出
来,陆续可能会把数据模型也整理出来。
Lastupdatedon2008‐09‐16
JavaandJ2EE
TheJavaClasspath
轻松玩转Ja
va配置的Classpath【转自www.bitsCN.com】
和 Java 类路径(classpath)打交道的过程中,开发者偶尔会遇到麻烦。这
是因为,类装载器实际装入的是哪一个类有时并不显而易见,当应用程序的
classpath 包含大量的类和目录时,情况尤其严重。本文将提供一个工具,它能
够显示出被装入类文件的绝对路径名。
一、Classpath 基础 网管联盟 bitsCN_com
Java 虚拟机(JVM)借助类装载器装入应用程序使用的类,具体装入哪些类
根据当时的需要决定。CLASSPATH 环境变量告诉类装载器到哪里去寻找第三方提
供的类和用户定义的类。另外,你也可以使用 JVM 命令行参数-classpath 分别
为应用程序指定类路径,在-classpath 中指定的类路径覆盖 CLASSPATH 环境变
量中指定的值。 网管网 www_bitscn_com
类路径中的内容可以是:文件的目录(包含不在包里面的类),包的根目录
(包含已打包的类),包含类的档案文件(比如。zip 文件或者。jar 文件)。在
Unix 家族的系统上,类路径的各个项目由冒号分隔,在 MS Windows 系统上,它
们由分号分隔。
网管网 www_bitscn_com
类装载器以委托层次的形式组织,每一个类装载器有一个父类装载器。当一
个类装载器被要求装载某个类时,它在尝试自己寻找类之前会把请求先委托给它
的父类装载器。系统类装载器,即由安装在系统上的 JDK 或 JRE 提供的默认类装
载器,通过 CLASSPATH 环境变量或者-classpath 这个 JVM 命令行参数装入第三
方提供的类或者用户定义的类。系统类装载器委托扩展类装载器装入使用 Java
Extension 机制的类。扩展类装载器委托自举类装载器(bootstrap class
loader)装入核心 JDK 类。 网管 bitscn_com
你可以自己开发特殊的类装载器,定制 JVM 如何动态地装入类。例如,大多
数 Servlet 引擎使用定制的类装载器,动态地装入那些在 classpath 指定的目录
内发生变化的类。 网管网 www_bitscn_com
必须特别注意的是(也是令人吃惊的是),类装载器装入类的次序就是类在
classpath 中出现的次序。类装载器从 classpath 的第一项开始,依次检查每一
个设定的目录和压缩文件,尝试找出待装入的类文件。当类装载器第一次找到具
有指定名字的类时,它就把该类装入,classpath 中所有余下的项目都被忽略。
网管下载 dl.bitscn.com
看起来很简单,对吧?
网管下载 dl.bitscn.com
二、可能出现的问题 网管联盟 bitsCN@com
不管他们是否愿意承认,初学者和富有经验的 Java 开发者都一样,他们都
曾经在某些时候(通常是在那些最糟糕的情形下)被冗长、复杂的 classpath
欺骗。应用程序所依赖的第三方类和用户定义类的数量逐渐增长,classpath 也
逐渐成了一个堆积所有可能的目录和档案文件名的地方。此时,类装载器首先装
载的究竟是哪一个类也就不再显而易见。如果 classpath 中包含重复的类入口,
这个问题尤其突出。前面已经提到,类装载器总是装载第一个它在 classpath
中找到的具有合适名字的类,从实际效果看,它“隐藏”了其他具有合适名字但
在 classpath 中优先级较低的类。
网管下载 dl.bitscn.com
如果不小心,你很容易掉进这个 classpath 的陷阱。当你结束了一天漫长的
工作,最后为了让应用程序使用最好、最新的类,你把一个目录加入到了
classpath,但与此同时,你却忘记了:在 classpath 的另一个具有更高优先级
的目录下,存放着该类的另一个版本! 网管联盟 bitsCN@com
三、一个简单的 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 网 管 下 载
dl.bitscn.com
'/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.cl
ass'
四、JWhich 的工作过程
要精确地测定 classpath 中哪一个类先被装载,你必须深入到类装载器的思
考方法。事实上,具体实现的时候并没有听起来这么复杂——你只需直接询问类
装载器就可以了!
1: public class JWhich {
中国网管联盟 bitsCN.com
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("\nClass '" + className +
23: "' found in \n'" + classUrl.getFile() + "'");
24: } else {
25: System.out.println("\nClass '" + className +
26: "' not found in \n'" +
27: System.getProperty("java.class.path") + "'");
28: }
29: }
30:
31: public static void main(String args[]) { 网管联盟 bitsCN@com
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 行)。 网管联盟 bitsCN@com
很容易想象,在使用 Servlet 引擎 classpath 的 Java Servlet 中,或者在
使用 EJB 服务器 classpath 的 EJB 组件中,上面这段简单的代码是如何运作。例
如,如果 JWhich 类是由 Servlet 引擎的定制类装载器装入,那么程序将用
Servlet 引擎的类装载器去寻找指定的类。如果 Servlet 引擎的类装载器不能找
到类文件,它将委托它的父类装载器。一般地,当 JWhich 被某个类装载器装入
时,它能够找出当前类装载器以及所有其父类装载器所装入的所有类。