Java Tools

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  83 随笔 :: 0 文章 :: 16 评论 :: 0 Trackbacks

#

2005年11月30日,BEA宣布,将与IBM、Oracle、SAP、Iona、Siebel和Sybase一起,支持一种构建和包装应用程序的新规范,即Service Component Architecture(服务组件架构,SCA)。

什么是SCA?

  SCA是一种规范,它使开发人员可以将注意力集中在业务逻辑的编写上。更直接地说,它是一种大大改进了的部署描述符,它可以使用任何语言而不限于Java。此外,您还可以使用编程式语言和声明式语言,比如BPEL和XSLT。SCA的特别之处在于,它对安全性、事务和可靠消息传递之类的特性使用了声明式策略的理念。

  使SCA脱颖而出的是,它是专门针对SOA设计的,而不像J2EE只是面向SOA做了修改。SCA关注的是如何描述按照各种编程模型和协议编写的组件所组成的程序集。
SCA的目标与BEA的目标是一致的:使事情(这次是应用程序的构建)更容易。SCA允许开发应用程序集而不考虑特定的中间件API或具体语言。

  SCA的核心概念是服务及其相关实现。服务由接口定义,而接口包含一组操作。服务实现可以引用其他服务,称为引用。服务可以有一个或多个属性,这些属性是可以在外部配置的数据值。

  SCA中的一个关键推动因素是Service Data Object(服务数据对象,SDO)。AquaLogic Data Services Platform一直在使用它。SDO用于表示业务数据、参数以及服务调用的返回值,当它遍历服务网络时,它还是一种表示数据的方式。注意,也可以使用XMLBeans及其它技术。

  SCA组件被组成为程序集。程序集是服务级的应用程序,它是服务的集合,这些服务被连接在一起,并进行了正确的配置。SCA程序集运行在两个级别:第一种情况,程序集是系统内的一组松散连接的组件;另一种情况,程序集是模块内的一组松散连接的组件。二者的区别在于,一般来说,模块是组件的集合,而系统是模块的集合。此外,系统对应于“大规模编程”(programming in the large或megaprogramming),而模块对应于“小规模编程”(programming in the small),比如构建当今的典型应用程序。相关例子以及更详细的说明请参见Dev2Dev站点上的SCA白皮书。

  将组件连接到它所依赖的服务的方式就是服务网络“装配”的方式。程序集已经在许多技术和框架中广为应用,比如CORBA、J2EE、ATG Dynamo和Spring,也就是说,它并不是新出现的。从这些技术中我们可以知道,程序集提供了许多重要的优点,比如更轻松的迭代开发,以及避免使业务逻辑依赖于中间件容器。SCA使用程序集解决了许多SOA开发中的重要问题,包括:

  1. 业务逻辑与底层基础架构、服务质量和传输的分离。
  2. “小规模编程”与“大规模编程”的联系。
  3. 为架构的设计、编码和操作性部署在自底向上(bottom-up)和自顶向下(top-down)两种方法中来回切换提供了一种统一的方式。

对BEA来说,它意味着什么?

  SDO 2.0规范是整个SCA技术平台的一个组成部分,它将在AquaLogic Data Services Platform (ALDSP)产品家族(已经支持SDO 1.0)的下一个主版本中实现。BEA AquaLogic Data Services Platform完全是针对SOA构建的,它自3.0版本开始就采用SCA技术。它提供企业数据服务的自动创建和维护,这可以帮助客户获得更高的生产力,进行业务优化,并更快地创造价值。BEA AquaLogic Data Services Platform提供了一个捕获与数据访问和数据更新有关的逻辑的单一位置。数据服务层提供了对相关的不同实时数据的可重用的、简化了的访问。注意,BEA Workshop中即将包含SCA支持。还有,BEA Workshop可免费下载

为什么SCA如此重要?

  SCA具有重大意义,因为它是第一项承诺提供一个组合模型以启用服务网络并支持构建下一代面向服务应用程序的技术。这一领域的每一次革新,都会导致出现一个新的抽象层,从而产生一批新的应用程序。C允许我们构建不能在汇编程序中构建的应用程序,而C++允许我们构建不能使用C构建的应用程序,Java又允许我们构建不能使用C++构建的应用程序。所有这些都是SCA的先例,简单地说,SCA就是未来用于构建大规模企业组合应用程序的技术。

posted @ 2007-07-09 17:48 和田雨 阅读(173) | 评论 (0)编辑 收藏

使用Windows操作系统的朋友对Excel(电子表格)一定不会陌生,但是要使用Java语言来操纵Excel文件并不是一件容易的事。在Web应用日益盛行的今天,通过Web来操作Excel文件的需求越来越强烈,目前较为流行的操作是在JSP或Servlet 中创建一个CSV (comma separated values)文件,并将这个文件以MIME,text/csv类型返回给浏览器,接着浏览器调用Excel并且显示CSV文件。这样只是说可以访问到Excel文件,但是还不能真正的操纵Excel文件,本文将给大家一个惊喜,向大家介绍一个开放源码项目,Java Excel API,使用它大家就可以方便地操纵Excel文件了。

JAVA EXCEL API简介

Java Excel是一开放源码项目,通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。使用该API非Windows操作系统也可以通过纯Java应用来处理Excel数据表。因为是使用Java编写的,所以我们在Web应用中可以通过JSP、Servlet来调用API实现对Excel数据表的访问。

现在发布的稳定版本是V2.0,提供以下功能:

  • 从Excel 95、97、2000等格式的文件中读取数据;
  • 读取Excel公式(可以读取Excel 97以后的公式);
  • 生成Excel数据表(格式为Excel 97);
  • 支持字体、数字、日期的格式化;
  • 支持单元格的阴影操作,以及颜色操作;
  • 修改已经存在的数据表;

现在还不支持以下功能,但不久就会提供了:

  1. 不能够读取图表信息;
  2. 可以读,但是不能生成公式,任何类型公式最后的计算值都可以读出;




回页首


应用示例

1 从Excel文件读取数据表

Java Excel API既可以从本地文件系统的一个文件(.xls),也可以从输入流中读取Excel数据表。读取Excel数据表的第一步是创建Workbook(术语:工作薄),下面的代码片段举例说明了应该如何操作:(完整代码见ExcelReading.java)

import java.io.*;
            import jxl.*;
            … … … …
            try
            {
            //构建Workbook对象, 只读Workbook对象
            //直接从本地文件创建Workbook
            //从输入流创建Workbook
            InputStream is = new FileInputStream(sourcefile);
            jxl.Workbook rwb = Workbook.getWorkbook(is);
            }
            catch (Exception e)
            {
            e.printStackTrace();
            }
            

一旦创建了Workbook,我们就可以通过它来访问Excel Sheet(术语:工作表)。参考下面的代码片段:

//获取第一张Sheet表
            Sheet rs = rwb.getSheet(0);
            

我们既可能通过Sheet的名称来访问它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一点是下标从0开始,就像数组一样。

一旦得到了Sheet,我们就可以通过它来访问Excel Cell(术语:单元格)。参考下面的代码片段:

//获取第一行,第一列的值
            Cell c00 = rs.getCell(0, 0);
            String strc00 = c00.getContents();
            //获取第一行,第二列的值
            Cell c10 = rs.getCell(1, 0);
            String strc10 = c10.getContents();
            //获取第二行,第二列的值
            Cell c11 = rs.getCell(1, 1);
            String strc11 = c11.getContents();
            System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
            System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
            System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
            

如果仅仅是取得Cell的值,我们可以方便地通过getContents()方法,它可以将任何类型的Cell值都作为一个字符串返回。示例代码中Cell(0, 0)是文本型,Cell(1, 0)是数字型,Cell(1,1)是日期型,通过getContents(),三种类型的返回值都是字符型。

如果有需要知道Cell内容的确切类型,API也提供了一系列的方法。参考下面的代码片段:

String strc00 = null;
            double strc10 = 0.00;
            Date strc11 = null;
            Cell c00 = rs.getCell(0, 0);
            Cell c10 = rs.getCell(1, 0);
            Cell c11 = rs.getCell(1, 1);
            if(c00.getType() == CellType.LABEL)
            {
            LabelCell labelc00 = (LabelCell)c00;
            strc00 = labelc00.getString();
            }
            if(c10.getType() == CellType.NUMBER)
            {
            NmberCell numc10 = (NumberCell)c10;
            strc10 = numc10.getValue();
            }
            if(c11.getType() == CellType.DATE)
            {
            DateCell datec11 = (DateCell)c11;
            strc11 = datec11.getDate();
            }
            System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
            System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
            System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
            

在得到Cell对象后,通过getType()方法可以获得该单元格的类型,然后与API提供的基本类型相匹配,强制转换成相应的类型,最后调用相应的取值方法getXXX(),就可以得到确定类型的值。API提供了以下基本类型,与Excel的数据格式相对应,如下图所示:




每种类型的具体意义,请参见Java Excel API Document。

当你完成对Excel电子表格数据的处理后,一定要使用close()方法来关闭先前创建的对象,以释放读取数据表的过程中所占用的内存空间,在读取大量数据时显得尤为重要。参考如下代码片段:

//操作完成时,关闭对象,释放占用的内存空间
            rwb.close();
            

Java Excel API提供了许多访问Excel数据表的方法,在这里我只简要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API Document。

Workbook类提供的方法

1. int getNumberOfSheets()
获得工作薄(Workbook)中工作表(Sheet)的个数,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            int sheets = rwb.getNumberOfSheets();
            

2. Sheet[] getSheets()
返回工作薄(Workbook)中工作表(Sheet)对象数组,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            Sheet[] sheets = rwb.getSheets();
            

3. String getVersion()
返回正在使用的API的版本号,好像是没什么太大的作用。

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            String apiVersion = rwb.getVersion();
            

Sheet接口提供的方法

1) String getName()
获取Sheet的名称,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            String sheetName = rs.getName();
            

2) int getColumns()
获取Sheet表中所包含的总列数,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            int rsColumns = rs.getColumns();
            

3) Cell[] getColumn(int column)
获取某一列的所有单元格,返回的是单元格对象数组,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            Cell[] cell = rs.getColumn(0);
            

4) int getRows()
获取Sheet表中所包含的总行数,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            int rsRows = rs.getRows();
            

5) Cell[] getRow(int row)
获取某一行的所有单元格,返回的是单元格对象数组,示例子:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            Cell[] cell = rs.getRow(0);
            

6) Cell getCell(int column, int row)
获取指定单元格的对象引用,需要注意的是它的两个参数,第一个是列数,第二个是行数,这与通常的行、列组合有些不同。

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            Cell cell = rs.getCell(0, 0);
            

2 生成新的Excel工作薄

下面的代码主要是向大家介绍如何生成简单的Excel工作表,在这里单元格的内容是不带任何修饰的(如:字体,颜色等等),所有的内容都作为字符串写入。(完整代码见ExcelWriting.java)

与读取Excel工作表相似,首先要使用Workbook类的工厂方法创建一个可写入的工作薄(Workbook)对象,这里要注意的是,只能通过API提供的工厂方法来创建Workbook,而不能使用WritableWorkbook的构造函数,因为类WritableWorkbook的构造函数为protected类型。示例代码片段如下:

import java.io.*;
            import jxl.*;
            import jxl.write.*;
            … … … …
            try
            {
            //构建Workbook对象, 只读Workbook对象
            //Method 1:创建可写入的Excel工作薄
            jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
            //Method 2:将WritableWorkbook直接写入到输出流
            /*
            OutputStream os = new FileOutputStream(targetfile);
            jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
            */
            }
            catch (Exception e)
            {
            e.printStackTrace();
            }
            

API提供了两种方式来处理可写入的输出流,一种是直接生成本地文件,如果文件名不带全路径的话,缺省的文件会定位在当前目录,如果文件名带有全路径的话,则生成的Excel文件则会定位在相应的目录;另外一种是将Excel对象直接写入到输出流,例如:用户通过浏览器来访问Web服务器,如果HTTP头设置正确的话,浏览器自动调用客户端的Excel应用程序,来显示动态生成的Excel电子表格。

接下来就是要创建工作表,创建工作表的方法与创建工作薄的方法几乎一样,同样是通过工厂模式方法获得相应的对象,该方法需要两个参数,一个是工作表的名称,另一个是工作表在工作薄中的位置,参考下面的代码片段:

//创建Excel工作表
            jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
            

"这锅也支好了,材料也准备齐全了,可以开始下锅了!",现在要做的只是实例化API所提供的Excel基本数据类型,并将它们添加到工作表中就可以了,参考下面的代码片段:

//1.添加Label对象
            jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
            ws.addCell(labelC);
            //添加带有字型Formatting的对象
            jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
            jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
            jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
            ws.addCell(labelCF);
            //添加带有字体颜色Formatting的对象
            jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
            UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED);
            jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
            jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
            ws.addCell(labelCF);
            //2.添加Number对象
            jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
            ws.addCell(labelN);
            //添加带有formatting的Number对象
            jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
            jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
            jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
            ws.addCell(labelNF);
            //3.添加Boolean对象
            jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
            ws.addCell(labelB);
            //4.添加DateTime对象
            jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
            ws.addCell(labelDT);
            //添加带有formatting的DateFormat对象
            jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
            jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
            jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
            ws.addCell(labelDTF);
            

这里有两点大家要引起大家的注意。第一点,在构造单元格时,单元格在工作表中的位置就已经确定了。一旦创建后,单元格的位置是不能够变更的,尽管单元格的内容是可以改变的。第二点,单元格的定位是按照下面这样的规律(column, row),而且下标都是从0开始,例如,A1被存储在(0, 0),B1被存储在(1, 0)。

最后,不要忘记关闭打开的Excel工作薄对象,以释放占用的内存,参见下面的代码片段:

//写入Exel工作表
            wwb.write();
            //关闭Excel工作薄对象
            wwb.close();
            

这可能与读取Excel文件的操作有少少不同,在关闭Excel对象之前,你必须要先调用write()方法,因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文件中。如果你先关闭了Excel对象,那么只能得到一张空的工作薄了。

3 拷贝、更新Excel工作薄

接下来简要介绍一下如何更新一个已经存在的工作薄,主要是下面二步操作,第一步是构造只读的Excel工作薄,第二步是利用已经创建的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段:(完整代码见ExcelModifying.java)

//创建只读的Excel工作薄的对象
            jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));
            //创建可写入的Excel工作薄对象
            jxl.write.WritableWorkbook  wwb = Workbook.createWorkbook(new File(targetfile), rw);
            //读取第一张工作表
            jxl.write.WritableSheet ws = wwb.getSheet(0);
            //获得第一个单元格对象
            jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
            //判断单元格的类型, 做出相应的转化
            if(wc.getType() == CellType.LABEL)
            {
            Label l = (Label)wc;
            l.setString("The value has been modified.");
            }
            //写入Excel对象
            wwb.write();
            //关闭可写入的Excel对象
            wwb.close();
            //关闭只读的Excel对象
            rw.close();
            

之所以使用这种方式构建Excel对象,完全是因为效率的原因,因为上面的示例才是API的主要应用。为了提高性能,在读取工作表时,与数据相关的一些输出信息,所有的格式信息,如:字体、颜色等等,是不被处理的,因为我们的目的是获得行数据的值,既使没有了修饰,也不会对行数据的值产生什么影响。唯一的不利之处就是,在内存中会同时保存两个同样的工作表,这样当工作表体积比较大时,会占用相当大的内存,但现在好像内存的大小并不是什么关键因素了。

一旦获得了可写入的工作表对象,我们就可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()方法,因为单元格已经于工作表当中,所以我们只需要调用相应的setXXX()方法,就可以完成更新的操作了。

尽单元格原有的格式化修饰是不能去掉的,我们还是可以将新的单元格修饰加上去,以使单元格的内容以不同的形式表现。

新生成的工作表对象是可写入的,我们除了更新原有的单元格外,还可以添加新的单元格到工作表中,这与示例2的操作是完全一样的。

最后,不要忘记调用write()方法,将更新的内容写入到文件中,然后关闭工作薄对象,这里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的。





回页首


小结

本文只是对Java Excel API中常用的方法作了介绍,要想更详尽地了解API,请大家参考API文档,或源代码。Java Excel API是一个开放源码项目,请大家关注它的最新进展,有兴趣的朋友也可以申请加入这个项目,或者是提出宝贵的意见。



参考资料

  1. Java Excel API 文档
  2. http://www.andykhan.com/jexcelapi/


关于作者

 

就叫我Rubber吧,我是一个Java迷,希望我们能成为朋友,我的Eamil: tim@trend.com.cn,我的联系电话0755-83501377

posted @ 2007-07-08 10:18 和田雨 阅读(245) | 评论 (0)编辑 收藏

大多数 Java 程序员都熟悉对 JAR 文件的基本操作。但是只有少数程序员了解 JAR 文件格式的 强大功能。在本文中,作者探讨了JAR 格式的许多功能和优势,包括打包、可执行的 JAR 文件、安全性和索引。

JAR 文件是什么?

JAR 文件格式以流行的 ZIP 文件格式为基础,用于将许多个文件聚集为一个文件。与 ZIP 文件不同的是,JAR 文件不仅用于压缩和发布,而且还用于部署和封装库、组件和插件程序,并可被像编译器和 JVM 这样的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用来指示工具如何处理特定的 JAR。

一个 JAR 文件可以用于:

  • 用于发布和使用类库
  • 作为应用程序和扩展的构建单元
  • 作为组件、applet 或者插件程序的部署单位
  • 用于打包与组件相关联的辅助资源

JAR 文件格式提供了许多优势和功能,其中很多是传统的压缩格式如 ZIP 或者 TAR 所没有提供的。它们包括:

  • 安全性。可以对 JAR 文件内容加上数字化签名。这样,能够识别签名的工具就可以有选择地为您授予软件安全特权,这是其他文件做不到的,它还可以检测代码是否被篡改过。
  • 减少下载时间。 如果一个 applet 捆绑到一个 JAR 文件中,那么浏览器就可以在一个 HTTP 事务中下载这个 applet 的类文件和相关的资源,而不是对每一个文件打开一个新连接。
  • 压缩。JAR 格式允许您压缩文件以提高存储效率。
  • 传输平台扩展。 Java 扩展框架(Java Extensions Framework)提供了向 Java 核心平台添加功能的方法,这些扩展是用 JAR 文件打包的(Java 3D 和 JavaMail 就是由 Sun 开发的扩展例子)。
  • 包密封。存储在 JAR 文件中的包可以选择进行 密封,以增强版本一致性和安全性。密封一个包意味着包中的所有类都必须在同一 JAR 文件中找到。
  • 包版本控制。一个 JAR 文件可以包含有关它所包含的文件的数据,如厂商和版本信息。
  • 可移植性。处理 JAR 文件的机制是 Java 平台核心 API 的标准部分。

压缩的和未压缩的 JAR

jar 工具(有关细节参阅 jar 工具 )在默认情况下压缩文件。未压缩的 JAR 文件一般可以比压缩过的 JAR 文件更快地装载,因为在装载过程中要解压缩文件,但是未压缩的文件在网络上的下载时间可能更长。

META-INF 目录

大多数 JAR 文件包含一个 META-INF 目录,它用于存储包和扩展的配置数据,如安全性和版本信息。Java 2 平台识别并解释 META-INF 目录中的下述文件和目录,以便配置应用程序、扩展和类装载器:

  • MANIFEST.MF。这个 manifest 文件定义了与扩展和包相关的数据。
  • INDEX.LIST。 这个文件由 jar 工具的新选项 -i 生成,它包含在应用程序或者扩展中定义的包的位置信息。它是 JarIndex 实现的一部分,并由类装载器用于加速类装载过程。
  • xxx.SF。 这是 JAR 文件的签名文件。占位符 xxx标识了签名者。
  • xxx.DSA。 与签名文件相关联的签名程序块文件,它存储了用于签名 JAR 文件的公共签名。

jar 工具

为了用 JAR 文件执行基本的任务,要使用作为Java Development Kit 的一部分提供的 Java Archive Tool ( jar 工具)。用 jar 命令调用 jar 工具。表 1 显示了一些常见的应用:

表 1. 常见的 jar 工具用法

功能 命令
用一个单独的文件创建一个 JAR 文件 jar cf jar-file input-file...
用一个目录创建一个 JAR 文件 jar cf jar-file dir-name
创建一个未压缩的 JAR 文件 jar cf0 jar-file dir-name
更新一个 JAR 文件 jar uf jar-file input-file...
查看一个 JAR 文件的内容 jar tf jar-file
提取一个 JAR 文件的内容 jar xf jar-file
从一个 JAR 文件中提取特定的文件 jar xf jar-file archived-file...
运行一个打包为可执行 JAR 文件的应用程序 java -jar app.jar





回页首


可执行的 JAR

一个 可执行的 jar 文件是一个自包含的 Java 应用程序,它存储在特别配置的JAR 文件中,可以由 JVM 直接执行它而无需事先提取文件或者设置类路径。要运行存储在非可执行的 JAR 中的应用程序,必须将它加入到您的类路径中,并用名字调用应用程序的主类。但是使用可执行的 JAR 文件,我们可以不用提取它或者知道主要入口点就可以运行一个应用程序。可执行 JAR 有助于方便发布和执行 Java 应用程序。

创建可执行 JAR

创建一个可执行 JAR 很容易。首先将所有应用程序代码放到一个目录中。假设应用程序中的主类是 com.mycompany.myapp.Sample 。您要创建一个包含应用程序代码的 JAR 文件并标识出主类。为此,在某个位置(不是在应用程序目录中)创建一个名为 manifest 的文件,并在其中加入以下一行:

Main-Class: com.mycompany.myapp.Sample
            

然后,像这样创建 JAR 文件:

jar cmf manifest ExecutableJar.jar application-dir
            

所要做的就是这些了 -- 现在可以用 java -jar 执行这个 JAR 文件 ExecutableJar.jar。

一个可执行的 JAR 必须通过 menifest 文件的头引用它所需要的所有其他从属 JAR。如果使用了 -jar 选项,那么环境变量 CLASSPATH 和在命令行中指定的所有类路径都被 JVM 所忽略。

启动可执行 JAR

既然我们已经将自己的应用程序打包到了一个名为 ExecutableJar.jar 的可执行 JAR 中了,那么我们就可以用下面的命令直接从文件启动这个应用程序:

java -jar ExecutableJar.jar
            





回页首


包密封

密封 JAR 文件中的一个包意味着在这个包中定义的所有类都必须在同一个 JAR 文件中找到。这使包的作者可以增强打包类之间的版本一致性。密封还提供了防止代码篡改的手段。

要密封包,需要在 JAR 的 manifest 文件中为包添加一个 Name 头,然后加上值为“true”的 Sealed 头。与可执行的 JAR 一样,可以在创建 JAR 时,通过指定一个具有适当头元素的 manifest 文件密封一个 JAR,如下所示:

Name: com/samplePackage/
            Sealed: true
            

Name 头标识出包的相对路径名。它以一个“/”结束以与文件名区别。在 Name 头后面第一个空行之前的所有头都作用于在 Name 头中指定的文件或者包。在上述例子中,因为 Sealed 头出现在 Name 头后并且中间没有空行,所以 Sealed 头将被解释为只应用到包 com/samplePackage 上。

如果试图从密封包所在的 JAR 文件以外的其他地方装载密封包中的一个类,那么 JVM 将抛出一个 SecurityException

扩展打包
扩展为 Java 平台增加了功能,在 JAR 文件格式中已经加入了扩展机制。扩展机制使得 JAR 文件可以通过 manifest 文件中的 Class-Path 头指定所需要的其他 JAR 文件。

假设 extension1.jar 和 extension2.jar 是同一个目录中的两个 JAR 文件,extension1.jar 的 manifest 文件包含以下头:

Class-Path: extension2.jar
            

这个头表明 extension2.jar 中的类是 extension1.jar 中的类的 扩展类。extension1.jar 中的类可以调用 extension2.jar 中的类,并且不要求 extension2.jar 处在类路径中。

在装载使用扩展机制的 JAR 时,JVM 会高效而自动地将在 Class-Path 头中引用的 JAR 添加到类路径中。不过,扩展 JAR 路径被解释为相对路径,所以一般来说,扩展 JAR 必须存储在引用它的 JAR 所在的同一目录中。

例如,假设类 ExtensionClient 引用了类 ExtensionDemo ,它捆绑在一个名为 ExtensionClient.jar 的 JAR 文件中,而类 ExtensionDemo 则捆绑在 ExtensionDemo.jar 中。为了使 ExtensionDemo.jar 可以成为扩展,必须将 ExtensionDemo.jar 列在 ExtensionClient.jar 的 manifest 的 Class-Path 头中,如下所示:

Manifest-Version: 1.0
            Class-Path: ExtensionDemo.jar
            

在这个 manifest 中 Class-Path 头的值是没有指定路径的 ExtensionDemo.jar,表明 ExtensionDemo.jar 与 ExtensionClient JAR 文件处在同一目录中。





回页首


JAR 文件中的安全性

JAR 文件可以用 jarsigner 工具或者直接通过 java.security API 签名。一个签名的 JAR 文件与原来的 JAR 文件完全相同,只是更新了它的 manifest,并在 META-INF 目录中增加了两个文件,一个签名文件和一个签名块文件。

JAR 文件是用一个存储在 Keystore 数据库中的证书签名的。存储在 keystore 中的证书有密码保护,必须向 jarsigner 工具提供这个密码才能对 JAR 文件签名。



图 1. Keystore 数据库
Keystore 数据库

JAR 的每一位签名者都由在 JAR 文件的 META-INF 目录中的一个具有 .SF 扩展名的签名文件表示。这个文件的格式类似于 manifest 文件 -- 一组 RFC-822 头。如下所示,它的组成包括一个主要部分,它包括了由签名者提供的信息、但是不特别针对任何特定的 JAR 文件项,还有一系列的单独的项,这些项也必须包含在 menifest 文件中。在验证一个签名的 JAR 时,将签名文件的摘要值与对 JAR 文件中的相应项计算的摘要值进行比较。



清单 1. 签名 JAR 中的 Manifest 和 signature 文件
Contents of signature file META-INF/MANIFEST.MF
            Manifest-Version: 1.0
            Created-By: 1.3.0 (Sun Microsystems Inc.)
            Name: Sample.java
            SHA1-Digest: 3+DdYW8INICtyG8ZarHlFxX0W6g=
            Name: Sample.class
            SHA1-Digest: YJ5yQHBZBJ3SsTNcHJFqUkfWEmI=
            Contents of signature file META-INF/JAMES.SF
            Signature-Version: 1.0
            SHA1-Digest-Manifest: HBstZOJBuuTJ6QMIdB90T8sjaOM=
            Created-By: 1.3.0 (Sun Microsystems Inc.)
            Name: Sample.java
            SHA1-Digest: qipMDrkurQcKwnyIlI3Jtrnia8Q=
            Name: Sample.class
            SHA1-Digest: pT2DYby8QXPcCzv2NwpLxd8p4G4=
            

数字签名

一个数字签名是.SF 签名文件的已签名版本。数字签名文件是二进制文件,并且与 .SF 文件有相同的文件名,但是扩展名不同。根据数字签名的类型 -- RSA、DSA 或者 PGP -- 以及用于签名 JAR 的证书类型而有不同的扩展名。

Keystore

要签名一个 JAR 文件,必须首先有一个私钥。私钥及其相关的公钥证书存储在名为 keystores 的、有密码保护的数据库中。JDK 包含创建和修改 keystores 的工具。keystore 中的每一个密钥都可以用一个别名标识,它通常是拥有这个密钥的签名者的名字。

所有 keystore 项(密钥和信任的证书项)都是用唯一别名访问的。别名是在用 keytool -genkey 命令生成密钥对(公钥和私钥)并在 keystore 中添加项时指定的。之后的 keytool 命令必须使用同样的别名引用这一项。

例如,要用别名“james”生成一个新的公钥/私钥对并将公钥包装到自签名的证书中,要使用下述命令:

keytool -genkey -alias james -keypass jamespass
            -validity 80 -keystore jamesKeyStore
            -storepass jamesKeyStorePass
            

这个命令序列指定了一个初始密码“jamespass”,后续的命令在访问 keystore “jamesKeyStore”中与别名“james”相关联的私钥时,就需要这个密码。如果 keystore“jamesKeyStore”不存在,则 keytool 会自动创建它。

jarsigner 工具

jarsigner 工具使用 keystore 生成或者验证 JAR 文件的数字签名。

假设像上述例子那样创建了 keystore “jamesKeyStore”,并且它包含一个别名为“james”的密钥,可以用下面的命令签名一个 JAR 文件:

jarsigner -keystore jamesKeyStore -storepass jamesKeyStorePass
            -keypass jamespass -signedjar SSample.jar Sample.jar james
            

这个命令用密码“jamesKeyStorePass”从名为“jamesKeyStore”的 keystore 中提出别名为“james”、密码为“jamespass”的密钥,并对 Sample.jar 文件签名、创建一个签名的 JAR -- SSample.jar。

jarsigner 工具还可以验证一个签名的 JAR 文件,这种操作比签名 JAR 文件要简单得多,只需执行以下命令:

jarsigner -verify SSample.jar
            

如果签名的 JAR 文件没有被篡改过,那么 jarsigner 工具就会告诉您 JAR 通过验证了。否则,它会抛出一个 SecurityException , 表明哪些文件没有通过验证。

还可以用 java.util.jarjava.security API 以编程方式签名 JAR(有关细节参阅 参考资料)。也可以使用像 Netscape Object Signing Tool 这样的工具。





回页首


JAR 索引

如果一个应用程序或者 applet 捆绑到多个 JAR 文件中,那么类装载器就使用一个简单的线性搜索算法搜索类路径中的每一个元素,这使类装载器可能要下载并打开许多个 JAR 文件,直到找到所要的类或者资源。如果类装载器试图寻找一个不存在的资源,那么在应用程序或者 applet 中的所有 JAR 文件都会下载。对于大型的网络应用程序和 applet,这会导致启动缓慢、响应迟缓并浪费带宽。

从 JDK 1.3 以后,JAR 文件格式开始支持索引以优化网络应用程序中类的搜索过程,特别是 applet。JarIndex 机制收集在 applet 或者应用程序中定义的所有 JAR 文件的内容,并将这些信息存储到第一个 JAR 文件中的索引文件中。下载了第一个 JAR 文件后,applet 类装载器将使用收集的内容信息高效地装载 JAR 文件。这个目录信息存储在根 JAR 文件的 META-INF 目录中的一个名为 INDEX.LIST 的简单文本文件中。

创建一个 JarIndex
可以通过在 jar 命令中指定 -i 选项创建一个 JarIndex。假设我们的目录结构如下图所示:



图 2. JarIndex
JarIndex Demo

您将使用下述命令为 JarIndex_Main.jar、JarIndex_test.jar 和 JarIndex_test1.jar 创建一个索引文件:

jar -i JarIndex_Main.jar JarIndex_test.jar SampleDir/JarIndex_test1.jar
            

INDEX.LIST 文件的格式很简单,包含每个已索引的 JAR 文件中包含的包或者类的名字,如清单 2 所示:



清单 2. JarIndex INDEX.LIST 文件示例
JarIndex-Version: 1.0
            JarIndex_Main.jar
            sp
            JarIndex_test.jar
            Sample
            SampleDir/JarIndex_test1.jar
            org
            org/apache
            org/apache/xerces
            org/apache/xerces/framework
            org/apache/xerces/framework/xml4j
            





回页首


结束语

JAR 格式远远超出了一种压缩格式,它有许多可以改进效率、安全性和组织 Java 应用程序的功能。因为这些功能已经建立在核心平台 -- 包括编译器和类装载器 -- 中了,所以开发人员可以利用 JAR 文件格式的能力简化和改进开发和部署过程。.



参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 参阅 jar 实用程序的命令行选项的文档。


  • Raffi Krikorian 在 ONJava 上发表的文章提供了有关 programmatically signing a JAR file的帮助。


  • 这篇关于 Java Archive Tool的文档解释了创建和操作 JAR 文件可以使用的选项。


  • 文章“ Java Web Start”( developerWorks,2001 年 9 月)描述了如何使用这种技术,以便允许应用程序可以指定所需的 JAR 文件并动态下载它们。


  • 有关 J2EE 服务器(比如 IBM WebSphere Application Server)使用 JAR 文件格式的不同方式,请参阅“ What are Java Archive (JAR) files?


  • JAR 格式是 WAR (Web Archive) 格式的基础,WAR用于在 J2EE 容器中部署 Servlet 和 JSP 应用程序。更多内容请参阅“ What are WAR files?”。


  • JAR 格式也是 EAR (Enterprise Archive) 格式的基础,EAR 用于在 J2EE 容器中部署 EJB。更多内容请参阅“ What are Enterprise Archive (EAR) files?”。


  • developerWorks Java 技术专区 上可以找到数百篇关于 Java 编程的各个方面的文章。



作者简介

Photo of Pagadala Suresh

Pagadala J. Suresh 是 IBM Global Services India 的软件工程师。他擅长的领域包括 Java 技术、WebSphere Application Server 和 WebSphere Studio Application Developer (WSAD)、Ariba Buyer。他参与了 WebSphere 的 IBM Redbook 项目。可以通过 pjsuresh@in.ibm.com 与 Pagadala 联系。


Photo of Palaniyappan Thiagarajan

Palaniyappan Thiagarajan 是位于印度班加罗尔的 IBM Global Services India 的软件工程师。他是IBM 认证的 IBM WebSphere Application Server V3.5 和 IBM DB2 UDB V7.1 Family Fundamentals 专家。可以通过 tpalaniy@in.ibm.com 与 Palaniyappan 联系。

posted @ 2007-07-08 10:17 和田雨 阅读(211) | 评论 (0)编辑 收藏

2001 年 11 月,IBM 向开源社区捐献了 Eclipse 代码,自此 Eclipse 在开发者社区得到飞速发展。2006 年 1月19日,Eclipse 国际高峰论坛也首次登陆中国,在清华大学信息科学技术大楼举行。在这次 Eclipse 大会上 developerWorks 编辑团队与 IBM 的 Eclipse 策略经理 John Kellerman 进行了座谈,聆听了他对 Eclipse 历史的追溯、对 Eclipse 架构的分析、对 Eclipse 现状分析、对未来发展的展望。

John Kellerman,IBM 软件部 Rational 产品总经理John Kellerman,IBM 软件部 Rational 产品总经理,与 1984 年加入 IBM, 一直从事应用开发工作。2000 年初,John 开始加入了 Eclipse 项目,现在任职 IBM Eclipse 策略部总经理。他现在的主要工作是在 Eclipse 基金会、Eclipse 社区中代表 IBM 的利益,与 Eclipse 成员公司合作。

developerWorks: IBM 开发工具的发展经历了从最初的 Visual Age到今天基于 Eclipse 3.0 的产品, 可以说是非常大的飞跃,您可否给我们谈一下这其中的历史?

John: 我在加入 Eclipse 项目之前,是 IBM Visual Age for SmallTalk 的产品经理。Eclipse 起源的一个非常重要的原因是,当时IBM 面临着一些挑战需要去解决,即 IBM 开发工具 Visual Age for Java 和 WebSphere Studio 很难集成到一起,而且底层的技术比较脆弱,因此进一步发展非常艰难,无法满足业界应用开发的需求。
因此,1998 年,我们开始了下一代开发工具技术探索之路,成立了一个项目开发小组,经过两年的发展,2000年,我们决定给新一代开发工具项目命名为 Eclipse,Eclipse 叫法当时只是内部使用的名称。这时候的商业目标就是希望 Eclipse 项目能够吸引开发人员,能发展起一个强大而又充满活力的商业合作伙伴(独立软件供应商)社区。同时我们意识到需要用它来对抗 Microsoft Visual Studio 的发展,因此从商业目标考虑,通过开源的方式我们最有机会达到目的。此外,IBM 推出了 Eclipse 试用计划,允许 IBM 以外的开发人员使用 Eclipse。结果证明我们的决策非常正确,Eclipse 从此在开发社区广为流传。Eclipse 也就成为这个项目的正式名称。

我们认为开源 Eclipse 不能只是简单地贡献出源码,创建一个开源项目然后放在开源许可证下;我们需要建立起多家公司的合作关系,否则就不足信服,不是真正的开源项目,而只是 IBM 的一个项目。因此在 2000 年到 2001 年之间,我做的主要工作就是去拜访一些公司,与他们谈论 Eclipse,邀请他们加入 Eclipse 项目。 您可以想象,这是非常有趣的谈话,最终,我们与 8 家公司达成了一致,其中包括一些 IBM 的竞争对手 WebGain、Borland, 还有一些盟友如 Rational(Rational 当时还没有被 IBM 收购)。

2001 年 12 月,IBM 向世界宣布了两件事,第一件是创建开源项目,既 IBM 捐赠价值 4 千万美元的源码给开源社区;另外一件事是成立 Eclipse 协会(Eclipse Consortium),这个协会由一些成员公司组成,主要任务是支持并促进 Eclipse 开源项目。

从此,我们看到了 Eclipse 本身、会员、插件(plug-in)和 Eclipse 社区飞速成长。2003 年,我们意识到这种会员模式很难进一步扩展,有些事务操作起来很困难,比如无法筹集赠款、无法成立合法理事会,这主要是因为 Eclipse 协会不是一个法律上的实体;此外,尽管 Eclipse 成功有目共睹,但仍然有些业界成员没有加入,他们认为是 Eclipse 的真正领导者是 IBM。因此我们认识到创建一个独立于 IBM 的 Eclipse 将会对 Eclipse 的发展非常有利, 于是 IBM 与其他成员公司合作起草了管理条例,准备成立 Eclipse 基金会(Eclipse Foundation)。2004 年初,Eclipse 基金会正式成立。

developerWorks: Eclipse 由一个很小的核心和核心之上的大量插件组成,这种插件式架构给当时的开发社区带来耳目一新的冲击。您能给我们介绍一下 Eclipse 插件架构吗?

John: 对于 Eclipse 插件架构,一个很好的比喻是 Web 浏览器,它也有插件,Eclipse 插件和浏览器相似,都是要解决扩展性问题。对于一个 Web 浏览器,如果你想既能浏览多媒体动画,又想浏览 PDF 文件,那么你就需要两个不同的插件,才能保证两种内容都能在 Web 浏览器中显示。 Eclipse 也一样,通过它的插件模式我们可以安装不同的插件来进行 HTML 的编辑、数据库的设计、Java 开发、C++ 开发等等,所有这些开发工作完全在一个 Eclipse 安装平台上进行。而且 Eclipse 模型所能做的远超过了这些,理解 Eclipse 插件的关键是首先要理解 Eclipse 扩展点,正是这个扩展点帮您在 Eclipse 中添加新功能。例如,如果希望在菜单中添加一项,就是通过 Eclipse 扩展点实现的。

所以当 Eclipse 插件实现了一个扩展点,就创建了一个扩展,此外,扩展还可以创建自己的扩展点。这种插件模式的扩展和扩展点是递归的,而且被证明是非常灵活的。记得当我们着手对 Eclipse 开发进行区域性调查时,最初的决定之一是:"We will eat our own dog food"(编者注:谚语,指一个公司在日常运行中使用自己的产品),即 Eclipse 团队要使用 Eclipse 进行 Eclipse 的开发。事实上,Eclipse 本身是一个核心的运行时绑定了一些在插件之上构建的插件,换句话说,Eclipse 本身就是一系列的插件。这样随着使用 Eclipse 构建 Eclipse 的经验累积,这种插件模式就变得日臻成熟。

developerWorks: IBM 对 Eclipse 已经投入了非常多的资源,IBM 在 Eclipse 的将来会扮演什么角色呢?

John: 目前 Eclipse 项目有 100 多个,IBM 参与了其中超过一半的项目,有 3 个主管在主持 Eclipse 相关的工作。2005 年我们还赞助了一些 Eclipse 创新基金和 Eclipse 奖学金。IBM 承诺在未来的几年内会一如既往地参与 Eclipse 研究,我们将会看到 IBM 研发出新项目,而在某些 Eclipse 项目上减少参与,这要看 IBM 的商业目标。最近我们提议了一个项目叫 Beacon, 是关于 Eclipse 流程框架的。我们将捐献一些 Rational Unified Process (RUP) 技术,基本上这个提议是要提供流程方面的内容。IBM 现在与一家叫做 Zend 的公司合作,希望在 Eclipse 上开发出用于 PHP 的开发工具。从商业方面看 IBM, 您会发现 Eclipse 是 Rational 软件开发平台的基础。此外由于 Eclipse 的插件模式、扩展性、集成性,我们为Rational, WebSphere, Lotus 和 Tivoli 构建了统一的平台。

developerWorks: 目前 IBM 的五大旗舰品牌都有基于 Eclipse 的产品, 这对 IBM 有什么好处?

John: 我们的目标是创建集成统一平台,我们有基于 Eclipse 构建的 DB2, Rational, WebSphere 等工具,这样客户就可以共同使用这些工具,有统一的界面风格,使用起来非常方便。对 IBM 自身来说,各个开发部门就可以关注于软件的核心性能开发,而不必要浪费资源做一些重新实现的工作,从而 DB2 开发部门就专注于数据库开发、WebSphere 专注应用服务器的开发、Rational 专注于建模、源代码管理,至于其他方面的辅助开发对于 Eclipse 来说是共同的。

developerWorks: Eclipse 是一个开源 IDE, 那如何通过 Eclipse 创造商业利益呢?

John: 对于 IBM, 在 Eclipse 技术开发方面与上百家公司合作,因为 IBM 看到了 Eclipse 作为通用的、开放的、标准的技术基础,其价值日益成长。当然,这种基础技术是不会带来创收的,没有人可以使用 Eclipse 技术来赚钱,但这并不重要,就像 Borland, Sybase 等公司都在花费钱做同样的构建工作,当然我们在这方面还有合作,但是我们还专注于将我们的核心功能组件构建在 Eclipse 平台上,例如,IBM 的 Rational Application Developer, 它是使用 Eclipse 作为基础,然后在这基础上添加了建模插件、Web 开发插件,还有其它更有价值的插件,这些插件我们是收费的。这就是使用 Eclipse 进行商业创收的模式之一。
此外,人们还可以直接为 Eclipse 构建插件,然后出售这些插件来赚钱,因为插件可以使用商业许可证发行。事实上我们建立 Eclipse 公共许可证,就是为了让 Eclipse 能很好的被商业采用,从而可以用它来赚钱,Eclipse 的所有成员公司基本都是商业软件提供商。目前全球有上百万开发人员在使用 Eclipse, 因此中国开发人员完全可以构建有价值的插件和工具,然后进行销售。同时 IBM 有一个合作伙伴计划,帮助合作伙伴宣传在 IBM 基于 Eclipse 的产品之上构建的应用。

developerWorks: 富客户端平台(RCP)目前是比较流行的应用开发模式,请谈一下 Eclipse 是如何支持富客户端的?

John: 在 Eclipse 的早期,有些用户试图除了将 Eclipse用于开发工具基础平台外,还用作更普遍的商业应用基础。他们认为一个 IDE 就是一个特殊的富客户端应用。这就是开放源码开发的魅力所在,当这些用户试着用 Eclipse 作为通用富客户端时,他们就把这些想法建议反馈给 Eclipse 开发小组。就这样 Eclipse 经过了 2.0 到 2.1 的发展,不断收到来自社区的建议和反馈,终于到了一个通用化的阶段。在3.0 版发行时,我们觉得时机成熟,于是正式声明将 Eclipse 作为通用的富客户端和 IDE。

其实最早的反馈是来自我们的 Lotus 开发小组,他们在 Eclipse 3.0 之前就意识到可以使用 Eclipse 来提供他们 Workplace 客户端的富客户端版本。从 Eclipse 3.0 到 3.1 再到 3.2, 我们看到富客户端平台应用的快速增长,同时也收到越来越多反馈帮我们完善提高。

我最欣赏的 RCP 应用之一是在EclipseCon 2005 上的一个演示,演示的是美国国家航空航天管理局(NASA)的一个项目,当时 NASA 在加利福尼亚州有个实验室叫 Jet Propulsion Laboratory (JPL),负责火星探测计划,他们的管理用户界面就是一个 Eclipse RCP 应用,通过这个应用,加利福尼亚州的工作人员就可以控制在火星上运行的火星车。在演示过程中,有人问为什么使用 Eclipse, 回答是,使用 Eclipse 这门技术,他们不用担心,而且还节省了不少纳税人的钱,因为他们只需要集中资源开发控制火星车的应用就可以了。

developerWorks: Eclipse 现在非常热门,您认为从微软的开发平台转到 Eclipse 上容易吗?

John: 这个问题可以从两方面来说明,一是软件提供商从 Visual Studio 移到 Eclipse 上,另一方面是微软平台上开发的程序的移植。

Eclipse 提供了不同的集成方法,可以是非常轻量级的集成,即只是简单的调用和返回。Eclipse可以处理 ActiveX 控件,这样 Word, Excel 就可以在 Eclipse 里打开,这是另一种集成模式。有些供应商选择把他们的工具封装在 Java 层,即保留 C/C++ 工具,封装在 Java 里与 Eclipse 进行通信。如果软件供应商希望将工具构建成 Eclipse 插件,那么我们通常建议在 Eclipse 中用 Java 重新实现,因为几乎所有的微软工具都是 C/C++ 工具。因此工具的迁移可以分三步走:轻量级集成、重量级集成和重新实现。

如果要迁移程序,Eclipse 提供了一些设施。但难易程度取决于程序是用什么工具开发的,因为最重要的部分是(版本控制系统的)代码库本身,如果代码库同时支持 Visual Studio 和 Eclipse, 那就容易的多。此外有一些公司提供了迁移系统,在 Eclipse 网站的社区部分中可以查看这些公司列表。

developerWorks: 对个体开发人员,他们如何能够参与 Eclipse 项目?

John: Eclipse 是完全开放的,任何人都可以参与,参与的最简单方法是关注它的新闻组,选择一个感兴趣的技术领域,然后下载代码,自己做些实验,并在新闻组上回答一些问题,这样随着技术的提高,就可以向 Bugzilla 提交 bug 和修复包,或去修复其他 bug。如果得到项目工作组的认可,便可以承担项目的一些义务,成为项目开发组的真正成员。此外还可以写一些文章发送到 Eclipse 组织,参加本地的 Eclipse 用户组等等方式。

如果有兴趣成立一个 Eclipse 开源项目,Eclipse 上有一个文档"Eclipse Development Process",描述了如何操作。

developerWorks: 最后请给我们谈谈 Eclipse 下个版本的情况?

John: Eclipse 3.2 计划于今年 6 月面世,名称是 Callisto, 选择 Callisto 作为名称是按社区要求的,因为这次目标是同时发布 10 个主要的 Eclipse 项目,以支持成员公司生态系统需求,他们将要把 Eclipse 框架集成到各自的软件产品中。

developerWorks: 感谢您接受我们的采访。

posted @ 2007-07-08 10:03 和田雨 阅读(242) | 评论 (0)编辑 收藏

使用 Java 语言所进行的面向对象编程变得空前普及。它使软件开发发生了某种程度上的变革,但最近的研究表明,有半数软件开发项目滞后,而三分之一的项目则超出预算。问题不在于技术,而是开发软件所使用的方法。所谓的“轻量型”或“灵活”方式,与如 Java 这样的面向对象语言的威力和灵活性结合起来,提供了一种很有意思的解决方案。最常见的灵活方式称为极端编程(Extreme Programming)或者 XP,但许多人并不真正了解它。对 Java 项目使用 XP 可以大大增加成功的机会。本文提供了 XP 的概述,并解释了它为什么很重要 -- 不是传言,也没有骗局。

在过去的十年中,CEO 们在产生稳步增加的收入方面面临巨大的压力。他们通过在许多方面采取一系列举措来解决这一问题,例如缩小公司规模、外包、再工程、企业资源规划 (ERP) 等等。这些对低效率的解决措施让 S&P 500 中的许多企业在 90 年代末能够连续几年保持两位数的收入增长。但这种方式也带来了一些负面影响。

在 Gary Hamel 所著的 Leading the Revolution(请参阅 参考资料)一书中,他声称已有一些迹象证明传统企业模式的优势已不那么明显。我们必须找到一些替代方法来为收入的持续增长提供动力。他建议唯一能让公司继续增长的办法是进行一次彻底的创新。我们认为在软件开发领域中尤其需要这样。

企业问题

如果使用标准软件开发方法,那么即使在 Java 平台上进行开发,也要做好失望的准备。如图 1 所示,最近的研究表明,有一半项目将滞后,而三分之一的项目将超过预算。这一推测比 1979 年由美国总审计局的研究结果好不了多少。



图 1. 软件项目成功和失败,过去和现在
软件项目成功和失败的统计

如果我们希望这些数字有显著提高,则需要彻底创新的方法来开发软件。有两个主要因素影响现有的方法:

  • 惧怕失败
  • 对软件本质的误解

没有人打算失败。具有讽刺意味的是为使失败最小化而创建的方法是失败的。对软件的误解是问题的根源。恐惧实际上只是一种症状。现有的方法是由那些有良好愿望但忘记了软件中的“软”的那些聪明人所创建的。他们假定开发软件就象造桥。因此他们从各种设计规范中借鉴了一些适用于“硬”物体(例如桥梁)的最优方法。结果是基于 Big Design Up-front (BDUF) 思想的反映迟钝的开发方法,软件不堪一击,人们无法使用它们。





回页首


一种解决方案:灵活方法

最近发生了一些转变,从所谓的“重量型”方法转向了“轻量型”或“灵活”方法,例如 Crystal 方法、适应性软件开发和(当前最流行的)XP。所有这些过程都有这样一个事实,即需要人们共同来开发软件。成功的软件过程必须将人们的长处最大化,将他们的缺点最小化,因为优点和缺点毋庸质疑都存在。在我们看来,XP 最出色的地方在于它能够解决所有影响参加人员的互补力量。

XP 提供了十年来最大的一次机会,给软件开发过程带来彻底变革。就象 Peopleware 作家 Tom DeMarco 所说,“XP 是当今我们所处领域中最重要的一项运动。预计它对于目前一代的重要性就象 SEI 及其能力成熟度模型对上一代的重要性一样。”

XP 规定了一组核心价值和方法,可以让软件开发人员发挥他们的专长:编写代码。XP 消除了大多数重量型过程的不必要产物,通过减慢开发速度、耗费开发人员的精力(例如干特图、状态报告,以及多卷需求文档)从目标偏离。我们认识到一个称为“极端编程”的东西可能很难作为正式的开发过程推荐给管理层,但如果您的公司从事软件行业,您应该帮助管理层绕过其名称认识到 XP 可以提供的竞争优势。

Kent Beck 在他所著的 Extreme Programming Explained: Embrace Change一书中概括了 XP 的核心价值(请参阅 参考资料)。我们对它们进行了总结:

  • 交流。 项目的问题往往可以追溯到某人在某个时刻没有和其他人一起商量某些重要问题上。使用 XP,不交流是不可能的事。
  • 简单。 XP 建议您总是尽可能围绕过程和编写代码做最简单的事情。按照 Beck 的说法,“XP 就是打赌。它打赌今天最好做些简单的事...而不是做更复杂但可能永远也不会用到的事。”
  • 反馈。 更早和经常来自客户、团队和实际最终用户的具体反馈意见为您提供更多的机会来调整您的力量。反馈可以让您把握住正确的方向,少走弯路。
  • 勇气。 勇气存在于其它三个价值的环境中。它们相互支持。需要勇气来相信一路上具体反馈比预先知道每样事物来得更好。需要勇气来在可能暴露您的无知时与团队中其他人交流。需要勇气来使系统尽可能简单,将明天的决定推到明天做。而如果没有简单的系统、没有不断的交流来扩展知识、没有掌握方向所依赖的反馈,勇气也就失去了依靠。

XP 的方法将这些价值转换成开发人员每天应做的事情。这里没什么新鲜内容。多年以来,行业认识到 XP 方法是“最优方法”。实际上,XP 中的“极端”来自两方面:

  • XP 采取经过证明的业界最优方法并将其发挥到极致。
  • XP 将这些方法以某种方式进行结合,使它们产生的结果大于各部分的总和。

这是怎样的情景呢?代码复查是个好的做法,因此始终通过成对地编写代码来做到。测试也是个好的做法,因此总是通过在编写代码之前编写测试来做到。文档很少与代码保持一致,因此只做那些最需要的事,余下的部分则取决于明确编写的代码和测试。XP 不保证人们总做正确的事,但它允许人们这样做。它将这些“极端”方法以一种相互支持的方式结合起来,显著提高了速度和有效性。





回页首


XP 的十二种方法

XP 的十二种方法(如图 2 所示)将其定义为规则。让我们仔细研究每一个方法来对“执行 XP”表示什么有个更全面的理解。



图 2. XP 的十二种方法

规划策略
有些人会指责 XP 是一种美其名的剽窃,只是一群牛仔在没有任何规则的情况下将一个系统拼凑在一起。错。XP 是为数不多的几种承认您在开始时不可能事事通晓的方法之一。无论是用户还是开发人员都是随着项目的进展过程才逐步了解事物的。只有鼓励和信奉这种更改的方法才是有效方法。状态限定方法忽视更改。而 XP 则留心更改。它倾听所用的方法就是“规划策略”,一个由 Kent Beck 创造的概念。

这一方法背后的主要思想是迅速地制定粗略计划,然后随着事物的不断清晰来逐步完善。规划策略的产物包括:一堆索引卡,每一张都包含一个客户素材,这些素材驱动项目的迭代;以及对下一两个发行版的粗略计划,如 Kent Beck 和 Martin Fowler 在他们的 Planning Extreme Programming中描述的那样(请参阅 参考资料)。让这种形式的计划得以发挥作用的关键因素是让用户做企业决策,让开发小组做技术决策。如果没有这一前提,整个过程就会土崩瓦解。

开发小组要决定:

  • 估计开发一个素材要花多长时间
  • 使用各种技术选项所花费的成本
  • 团队组织
  • 每个素材的“风险”
  • 迭代中素材开发的顺序(先开发风险最大的那一个可以减轻风险)

客户需要决定:

  • 范围(一个发行版的素材和每一次迭代的素材)
  • 发行日期
  • 优先级(根据企业价值先开发哪些特性)

规划经常发生。这样,在客户或开发人员学习新事物的同时,就为他们调整计划提供了频繁机会。

成对编程
使用 XP,成对的开发人员编写所有产品代码。这种方式听上去好象缺乏效率。Martin Fowler 说,“当人们说成对编程降低生产力时,我回答,‘那是因为大多数耗费时间的编程部分是靠输入来完成的。’”实际上,成对编程无论在经济还是其它方面都提供了许多好处:

  • 所有设计决策都牵涉到至少两个人。
  • 至少有两个人熟悉系统的每一部分。
  • 几乎不可能出现两个人同时疏忽测试或其它任务。
  • 改变各对的组合在可以在团队范围内传播知识。
  • 代码总是由至少一人复查。

研究还显示成对的编程实际上比单独编程更有效(有关详细信息,请参阅 参考资料 中 Alistair Cockburn 和 Laurie Williams 所著的 The Costs and Benefits of Pair Programming)。

测试
在 XP 中有两种测试:

  1. 单元测试
  2. 验收测试

开发人员在他们编写代码的同时编写单元测试。客户在他们定义了素材后编写验收测试。单元测试及时告诉开发人员系统在某一点上是否“工作”。验收测试告诉团队系统是否执行用户希望它执行的操作。

假设团队使用的是如 Java 这样的面向对象语言,开发人员在为一些方法编写代码之前为每种有可能失败的方法编写单元测试。然后他们编写足够的代码使之能通过测试。有时人们会发现这有点奇怪。答案很简单。编写测试首先为您提供:

  • 一组可能最完整的测试
  • 可能能工作的最简单的代码
  • 代码意图的明确景象

开发人员只有在通过所有单元测试后才可以将代码检入到源代码资源库中。单元测试使开发人员有信心相信他们的代码能够工作。这为其他开发人员留下线索,可以帮助他们理解最早的开发人员的意图(实际上,这是我们看到过的最好的文档)。单元测试还给了开发人员勇气重新划分代码,因为测试失败可以立刻告诉开发人员存在错误。应该将单元测试自动化,并提供明确的通过/失败结果。xUnit 框架(请参阅 参考资料 )做到的远不止这些,因此大多数 XP 小组都使用它们。

用户负责确保每个素材都有验收测试来确认它们。用户可以自己编写测试、可以征募组织中的其他成员(例如 QA 人员或业务分析员)编写它们,也可以将这两种方法结合起来。测试告诉他们系统是否具有应该具有的那些特性,以及是否可以正确工作。理想情况下,用户在迭代完成之前就应该写好迭代中那些素材的验收测试了。应该将验收测试自动化,并要经常运行来确保开发人员在实现新特性时没有破坏任何现有的特性。通常情况下,客户需要来自开发团队的某些帮助来编写验收测试。我们对一个项目开发一个可重用的自动验收测试框架,可以让用户在简单编辑器中输入他们的输入和所期望的输出。框架将输入转换成 XML 文件、运行文件中的测试,然后为每个测试显示“通过”或“失败”。客户喜欢这一做法。

不是所有验收测试都必须在所有情况下通过。问题是验收测试帮助客户衡量项目“完成”的情况如何。它们还可以让客户获悉有关某些事物是否可以发行的决定。

重新划分
重新划分是在不更改功能性的前提下对代码加以改进。XP 小组在进行重新划分时毫不手软。

开发人员重新划分有两个重要时机:实现特性之前和之后。开发人员尝试确定更改现有代码是否可以让新特性的开发更容易。他们查看刚刚写好的代码,看是否有方法可以对它进行简化。例如,如果他们认为有抽象的机会,就会进行重新划分来从具体实现中除去重复的代码。

XP 建议您应该编写可能运行的最简单的代码,但同时也建议您应该不断学习。重新划分让您将学到的知识加入到代码中,同时又不会破坏测试。它使您的代码简练。这意味着它可以存在相当长的时间、为以后的开发人员引入更少问题,并为他们指引正确的方向。

简单的设计
XP 的诽谤者说该过程忽略了设计。事实不是这样。问题是重量型方法建议您做的不过是提前完成大部分琐碎的设计任务。这就象是拍一张静态的地平线的照片,静止不动,然后尝试画一张如何到达那里的完美的地图。XP 说设计不应该在事物将保持不变的前提下预先仓促进行。XP 认为设计非常重要,因此应该是一个持续的事务。我们总是先尝试使用能够工作的最简单的设计,然后随着现实的不断显现来更改它。

什么是可能工作的最简单的设计?它是符合以下条件的设计(感谢 Kent Beck 为我们一一列出):

  • 运行所有测试
  • 不包含重复代码
  • 明确陈述程序员对所有代码的意图
  • 包含尽可能最少的类和方法

对简单设计的需求并不是说所有设计都很小,也不表示它们是无足轻重的。它们只不过需要尽可能简单,但是仍能工作。不要包括不使用的额外特性。我们称这样的事物为 YAGNI,表示“您将不需要它(You Aren't Going to Need It)。”不要让 YAGNI 破坏您成功的机会。

集合体代码所有权
小组中的任何人都应该有权对代码进行更改来改进它。每个人都拥有全部代码,这意味着每个人都对它负责。这种技术可以让人们对部分代码进行必要的更改而不用经过代码拥有者个人的瓶颈。每个人都负责这一事实消除了无代码所有权所带来的混乱。

“每人拥有所有代码”与“没人拥有代码”的说法并不一样。没人拥有代码时,人们可以随处进行破坏而不必负任何责任。而 XP 说,“如果是您破坏的,应该您来弥补。”我们有一些必须在每次集成之前和之后运行的单元测试。如果您破坏了某些事物,您要负责进行修补,无论它位于代码的哪一部分。这需要极端规则。可能这是名称中带有“极端”的另一个原因。

持续的集成
经常进行代码集成可以帮助您避免集成梦魇。XP 团队在一天中集成了代码几次,每次都在所有单元测试对系统运行后执行。

传统方法工作方式如下:编写大量代码后执行一次大爆炸式的集成,然后花费相当长的时间来改正问题。这种笨拙的形式的确使项目速度减缓。大爆炸式的集成给团队立即带来大量问题,并且这些问题通常都有几百种可能的原因。

如果经常进行集成,任何特定集成失败的原因都会非常明显(以前运行过测试,因此错误一定是新事物犯下的)。使用这种方法,当遇到问题时,可能的原因就相当有限。修改起来更容易,花的时间少得多,使团队保持最快速度前进。

现场客户
要使功能最理想,XP 小组需要在现场有一位客户来明确素材,并做出重要的企业决策。开发人员是不允许单独做这些事情的。让客户随时在场可以消除开发人员等待决策时出现的瓶颈。

XP 不会假装素材卡是开发人员交付必要代码所需的唯一指示。素材是对以后在客户和开发人员之间填写细节的对话的一项承诺。与将所有要求写在一个静态文档中不同,其思想是进行面对面的交流,减少产生误解的机会。

我们发现让客户在现场是可能最好的一种情形,但这不是解决问题的唯一方案。底线是客户必须随时在需要回答问题和根据企业价值为团队提供指示时有空。如果客户并非在现场专职陪伴团队的情况下就能做到这些,那很好。如果能和团队待在一起,这会更方便,但只是建议而已。

小发行版
发行版应该尽可能地小,同时仍然提供足够的企业价值以证明它们值得。

只要觉得有意义就可以发布系统。这样就尽可能早为用户提供了价值(请记住,今天的钱比明天的钱来得值钱)。小发行版将为开发人员提供具体的反馈意见,告诉他们哪些符合客户需要,哪些不符合客户需要。然后,小组可以将这些经验教训包括在其下一发行版的规划中。

一周 40 小时
Kent Beck 说他希望“...每天早晨都感到有活力有激情,每天晚上都感到疲惫而满足。”一周 40 小时工作可以让您做到这一点。确切的小时数并不重要,重要的是原则。长时间地持续工作会扼杀工作绩效。疲劳的开发人员会犯更多错误,从长期来说,将比按“正常”时间表进行的开发慢得多。

即使开发人员可以在长时间很好工作,这也不意味着他们应该这样。最终他们会厌倦,会离开他们的工作,或者产生影响他们工作绩效的非工作问题。如果您打乱了人们的生活,将会尝到它所带来的恶果。加班并不是解决项目问题的答案。实际上,它是更大问题的症状。如果您要走向灭亡,就无药可救了。

编码标准
拥有编码标准有两个目的:

  • 防止团队被一些例如事物没有以最大速度发展这种无关紧要的愚蠢争论搞得不知所措。
  • 它支持其它方法。

如果没有编码标准,重新划分代码会更加困难,按应当的频度交换对更困难,快速前进也更困难。目标应该是团队中没有人辨认得出是谁写的哪一部分代码。以团队为单位对某一标准达成协议,然后遵守这一标准。目标不是创建一个事无巨细的规则列表,而是提供将确保您的代码可以清晰交流的指导方针。编码标准开始时应该很简单,然后根据团队经验逐步进化。不要预先花费太多时间。创建能够工作的最简单标准,然后逐步发展。

系统比喻
体系结构是做什么用的?它提供了系统各种组件以及它们是如何交互的画面 -- 一种映射,可以让开发人员了解新的代码部分适合放在哪里。

XP 中的系统比喻与大多数方法称作的体系结构差不多。比喻为团队提供了一致的画面,他们可以用它来描述现有系统的工作方式、新部件适合的位置,以及它们应该采取的形式。

重要的是要记住,关键要让每个人理解系统是如何组合在一起的,而不是美丽的比喻。有时您就是无法想到一个好的比喻。想到时就太好了。





回页首


一起工作的方法

整体大于各个部分之和。您可以实现单一方法或一小部分方法,比不使用任何方法得到更大收益。但您只能在实现所有方法的情况下获得最大收益,因为它们的力量来自它们之间的交互。

最初时按照书籍来执行 XP,作为基准。一旦理解了如何进行交互,就拥有了将它们适应于自身环境所需的知识。请记住,“进行 XP”不是目的,而是到达终点的一种手段。目标是快速地开发高级软件。如果您的过程有一些变异,已称不上是在进行 XP,但结果仍能让您战胜所有竞争对手,您已经成功了。





回页首


为什么 XP 很重要

坦率地说,XP(或者任何其它灵活方法)根本就不重要。它能够产生的 结果 才是关键。如果如 XP 这样的灵活方式可以帮助您更快地开发更好的软件而少受痛苦,那么它值得考虑。

还记得我们在这篇文章开始时提到的那些令人生畏的数字吗?我们相信使用 XP 开发面向对象软件可以有机会将它们变得更好。目前我们的经验已经证实了这一信念。



参考资料



作者简介

 

Roy W. Miller 是 RoleModel Software, Inc 的软件开发人员。在 RoleModel 期间,Roy 开发了基于 Java/XML 的自动验收测试框架,并为 Dallas Semiconductor 的 TINI Java 平台创建了几个应用的原型。在加盟 RoleModel 之前,他在 Andersen Consulting(现在是 Accenture)中服务了六年,用过他们的专用重量型商业集成方法 (MIB) 及其变体。自从加盟 RoleModel 后,他获得了许多有关 XP 以及对该方法的本地适应的经验。Roy 与他人合著了 Addison-Wesley XP 系列中的一本书( XP Applied ,将于 2001 年 10 月出版),并是在 XP2001 "Business of XP" 展示会上的一名特邀专题讨论小组成员。可以通过 rmiller@rolemodelsoft.com 与 Roy 联系。


 

Christopher T. Collins 是 RoleModel Software, Inc 的高级软件开发人员。在 RoleModel 期间,Chris 参与了运行将近两年的一个 XP 项目,为新的摩托罗拉蜂窝式电话平台开发了一个嵌入式 Java 应用程序,并将 JUnit 移植到 Sun 的 J2ME 平台上运行。在加盟 RoleModel 之前,他花了 5 年的时间使用许多不同的语言为一些组织开发软件,最近的一次是为美国国防部开发应用程序。他拥有使用和适应涉及几种灵活和重量型的不同开发方法的经验,包括 RUP 和 XP。Chris 拥有美国西佛罗里达大学计算机科学和软件工程的硕士学位,目前在北卡罗来纳州立大学教授 Java 编程课程。他曾是杜克大学有关 XP 的特邀演讲人,并将在 XP2001 上介绍有关过程适应的论文。通过 ccollins@rolemodelsoft.com 与 Chris 联系。

posted @ 2007-07-06 16:34 和田雨 阅读(183) | 评论 (0)编辑 收藏

2000 年 11 月 01 日

这是篇细探 JAXP,Sun 的 Java API for XML 的文章,帮助解除了有关 JAXP 本质和服务目的的疑惑。本文讲解了 JAXP 的基本概念,演示 XML 语法分析为什么需要 JAXP,并显示如何轻易更改 JAXP 使用的语法分析器。本文还进一步讲述了 SAX 和 DOM 这两个流行的与 JAXP 相关的 Java 和 XML API。

Java 和 XML 在每一个技术领域都制造了新闻,并且对于软件开发人员来说,似乎是 1999 年和 2000 年最重要的发展。结果,Java 和 XML API 的数量激增。其中两个最流行的 DOM 和 SAX 还引起极大兴趣,而 JDOM 和数据绑定 API 也接踵而来。只透彻理解这些技术中的一个或两个就是一项艰巨任务,而正确使用所有这些技术就会使您成为专家。但在去年,另一个 API 给人留下了深刻印象,它就是 Sun 的 Java API for XML,通常称为 JAXP。如果考虑到 Sun 在其平台上还没有任何特定于 XML 的产品,那么这个进展就不足为奇。而令人惊奇的是人们对 JAXP 了解的缺乏。多数使用它的开发人员在他们所用的这个 API 的概念理解上都有错误。

什么是 JAXP?

本文假设您有 SAX 和 DOM 的基本知识。这里实在没有足够篇幅来解释 SAX、DOM 和 JAXP。如果您是 XML 语法分析的新手,那么可能要通过联机资源阅读 SAX 和 DOM,或者浏览我的书。( 参考资源 一节中有至 API 和我的书的链接。)获得基本知识后再看本文会比较好。





回页首


API 还是抽象?

在讲解代码之前,介绍一些基本概念很重要。严格地说,JAXP 是 API,但是将其称为抽象层更准确。它不提供处理 XML 的新方式,不补充 SAX 或 DOM,也不向 Java 和 XML 处理提供新功能。(如果在这点上理解有误,则本文正好适合您!)它只是使通过 DOM 和 SAX 处理一些困难任务更容易。如果在使用 DOM 和 SAX API 时遇到特定于供应商的任务,它还使通过独立于供应商的方式处理这些任务成为可能。

虽然要分别讲述所有这些特性,但是真正需要掌握的是:JAXP 不提供语法分析功能 !没有 SAX、DOM 或另一个 XML 语法分析 API,就 无法分析 XML 语法 。有很多人曾让我将 DOM、SAX 或 JDOM 与 JAXP 进行对比。但进行这些对比是不可能的,因为前三个 API 与 JAXP 的目的完全不同。SAX、DOM 和 JDOM 都分析 XML 语法。而 JAXP 却提供到达这些语法分析器和结果的方式。它自身不提供分析文档语法的新方法。如果要正确使用 JAXP,则一定要弄清这点。这将使您比其它 XML 开发人员领先一大截。

如果仍然怀疑(或认为我故弄玄虚),请从 Sun 的 Web 站点下载 JAXP 分发(请参阅 参考资料 一节),然后就会知道基本 JAXP 是什么。在包括的 jar ( jaxp.jar ) 中 只有六个类 !这个 API 会有多难哪?所有这些类( javax.xml.parsers 包的一部分)都位于现有语法分析器之上。这些类中的两个还用于错误处理。JAXP 比人们想象的要简单得多。那么,为什么还感到困惑哪?





回页首


Sun 的 JAXP 和 Sun 的语法分析器

JAXP 下载时包括 Sun 的语法分析器。所有 parser 器类作为 com.sun.xml.parser 包和相关子包的一部分位于 parser.jar 档案中。应该知道,该语法分析器(代码名为 Crimson) 是 JAXP 自身的一部分。它是 JAXP 版本的一部分,但不是 JAXP API 的一部分。令人困惑吗?有一点。换这种方式想想:JDOM 与 Apache Xerces 语法分析器一起提供。该语法分析器不是 JDOM 的一部分,但由 JDOM 使用,所以包括它,以确保 JDOM 可以单独使用。JAXP 也是如此,但不象 JDOM 那样好表达:JAXP 与 Sun 的语法分析器一起提供,以便可以立即使用。但是,很多人将 Sun 的语法分析器中包括的类当成 JAXP API 的一部分。例如,新闻组中一个常见的问题是:“怎样使用 JAXP 中的 XMLDocument 类?其目的是什么?”这个答案可有些复杂。

首先, com.sun.xml.tree.XMLDocument 类不是 JAXP 的一部分。它是 Sun 语法分析器的一部分。所以,这个问题从一开始就给人以误导。其次,JAXP 的整个意义在于在处理语法分析器时提供供应商独立性。使用 JAXP 的同一代码可以与 Sun 的 XML 语法分析器、Apache 的 Xerces XML 语法分析器和 Oracle 的 XML 语法分析器一起使用。而使用特定于 Sun 的类是个坏主意。这与 JAXP 的整个意义相背离。现在看出来这个问题怎样混淆概念了吗?语法分析器和 JAXP 发行版本(至少是 Sun 的版本)中的 API 被混为一谈,开发人员将其中一个的类和特性当成是另一个的了,反之亦然。





回页首


旧和新

关于 JAXP,最后需要指出的是:使用 JAXP 有一些缺陷。例如,JAXP 只支持 SAX 1.0 和 DOM 第一层规范。SAX 2.0 从 2000 年 5 月起就完成,DOM 第二层规范支持甚至在大多数语法分析器中存在更长时间。DOM 第二层规范还没有完成,但确实足够稳定以用于生产。这两个 API 的新版本都有重大改进,最明显的是对 XML 名称空间的支持。该支持还允许“XML Schema 确认”,这个与 XML 相关的另一热门技术。公平地说,当 JAXP 发布 1.0 最终发行版时,SAX 2.0 和 DOM 第一层规范都还没有完成。但是,由于没有包括这些新版本,确实为开发人员带来很大不便。

还可以使用 JAXP,但是也可以等待 JAXP 1.1,它支持 SAX 2.0 和 DOM第二层规范 。否则,将发现,JAXP 提供的优点以 SAX 和 DOM 最新版本中的功能为代价,并使应用程序更加难以编码。无论是否等待下一个 JAXP 发行版,都要留意这个问题。如果将 JAXP 与语法分析器一起使用,而语法分析器支持的 DOM 和 SAX 版本比 JAXP 支持的要高,则可能会有类路径问题。所以,事先留意一下,并且,一旦有 JAXP 1.1,马上升级。基本理解 JAXP 之后,让我们看一下 JAXP 依赖的 API:SAX 和 DOM。





回页首


从 SAX 开始

SAX (Simple API for XML)是用于处理 XML 的事件驱动方法。它基本由许多回调函数组成。例如,每当 SAX 语法分析器遇到元素的开始标记时就调用 startElement() 。对于字符串,将调用 characters() 回调函数,然后在元素结束标记处调用 endElement() 。还有很多回调函数用于文档处理、错误和其它词汇结构。现在知道这是怎么回事了。SAX 程序员实现一个定义这些回调函数的 SAX 接口。SAX 还实现一个名为 HandlerBase 的类,该类实现所有这些回调函数,并提供所有这些回调方法的缺省空实现。(提到这一点是因为它在后面讲到的 DOM 中很重要。)SAX 开发人员只需扩展这个类,然后实现需要插入特定逻辑的方法。所以,SAX 的关键在于为这些不同的回调函数提供代码,然后允许语法分析器在适当的时候触发这些回调函数中的每一个。

因此,典型的 SAX 过程如下:

  • 用特定供应商的语法分析器实现创建一个 SAXParser 实例
  • 注册回调实现(例如,通过使用扩展 HandlerBase 的类)
  • 开始进行语法分析,然后在触发回调实现时等待

JAXP 的 SAX 组件提供执行所有这些步骤的简单方式。如果没有 JAXP,SAX 语法分析器要直接从供应商类(如 org.apache.xerces.parsers.SAXParser )进行实例化,或者必须使用名为 ParserFactory 的帮助类。第一个方法的问题很明显:不独立于供应商。第二个方法的问题在于类厂需要一个自变量,即要使用的语法分析器类的字符串名称(还是那个 Apache 类 org.apache.xerces.parsers.SAXParser )。可以通过将不同语法分析器作为 String 传递来更改语法分析器。使用这种方法不必更改任何 import 语句,但是还是要重新编译类。这显然不是最佳解决方案。如果能够不重新编译类而更改语法分析器,可能会简单得多,是不是这样呢?

JAXP 提供了更好的替代方法:它允许将语法分析器作为 Java 系统属性来提供。当然,当从 Sun 下载版本时,将得到使用 Sun 语法分析器的 JAXP 实现。可以从 Apache XML Web 站点下载在 Apache Xerces 上构建其实现的相同 JAXP 接口。因此(无论哪一种情况),更改正在使用的语法分析器需要更改类路径设置,即从一种语法分析器实现更改到另一个,但是 要求重新编译代码。这就是 JAXP 的魔力,或抽象性。

SAX 语法分析器一瞥

JAXP SAXParserFactory 类是能够轻易更改语法分析器实现的关键所在。必须创建这个类的新实例(等一会将讲到)。创建新实例之后,类厂提供一个方法来获得支持 SAX 的语法分析器。在内部,JAXP 实现处理依赖于供应商的代码,使您的代码不受影响。这个类厂还提供其它一些优秀特性。

除创建 SAX 语法分析器实例的基本工作之外,类厂还允许设置配置选项。这些选项影响所有通过类厂获得的语法分析器实例。JAXP 1.0 中两个可用的功能是设置名称空间敏感性 ( setNamespaceAware (boolean awareness)),和打开确认 ( setValidating (boolean validating))。请记住,一旦设置了这些选项,在调用该方法之后,它们将影响 所有从 类厂获得的实例。

设置了类厂之后,调用 newSAXParser() 将返回一个随时可用的 JAXP SAXParser 类实例。这个类封装了一个下层的 SAX 语法分析器(SAX 类 org.xml.sax.Parser 的实例)。它还防止向语法分析器类添加任何特定于供应商的附加功能。(还记得以前对 XmlDocument 的讨论吗?)这个类可以开始进行实际的语法分析。以下清单显示如何创建、配置和使用 SAX 类厂。



清单 1. 使用 SAXParserFactory
import java.io.File;
            import java.io.IOException;
            import java.io.OutputStreamWriter;
            import java.io.Writer;
            // JAXP
            import javax.xml.parsers.FactoryConfigurationError;
            import javax.xml.parsers.ParserConfigurationException;
            import javax.xml.parsers.SAXParserFactory;
            import javax.xml.parsers.SAXParser;
            // SAX
            import org.xml.sax.AttributeList;
            import org.xml.sax.HandlerBase;
            import org.xml.sax.SAXException;
            public class TestSAXParsing {
            public static void main(String[] args) {
            try {
            if (args.length != 1) {
            System.err.println ("Usage: java TestSAXParsing [filename]");
            System.exit (1);
            }
            // 获得SAX 语法分析器类厂
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //设置设置名称空间敏感性选项,关掉确认选项
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            SAXParser parser = factory.newSAXParser();
            parser.parse(new File(args[0]), new MyHandler());
            } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not support " +
            " the requested features.");
            } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining SAX Parser Factory.");
            } catch (Exception e) {
            e.printStackTrace();
            }
            }
            }
            class MyHandler extends HandlerBase {
            //通过 DocumentHandler, ErrorHandler等实现的SAX回调函数
            }
            

请注意,在这段代码中,在使用类厂时可能发生两个特定于 JAXP 的问题:无法获得或配置 SAX 类厂,以及无法配置 SAX 语法分析器。当无法获得 JAXP 实现中指定的语法分析器或系统属性时,通常会发生第一个问题 FactoryConfigurationError 。当正在使用的语法分析器中的特性不可用时,会发生第二个问题 ParserConfigurationException 。这两个问题都容易处理,应该不会对 JAXP 的使用造成任何困难。

在获得类厂、关闭名称空间并打开“确认”之后,将获得 SAXParser ,然后开始语法分析。请注意, SAX 语法分析器的 parse() 方法取得前面提到的 SAX HandlerBase 类的一个实例。(可以通过完整的 Java 清单 查看该类的实现 。)还要传递要进行语法分析的文件。但是, SAXParser 所包含的远不止这一个方法。

使用 SAX 语法分析器

获得 SAXParser 类的实例之后,除了向语法分析器传递 File 进行语法分析之外,还可以用它做更多的事。由于如今大型应用中的应用程序组件之间通信方式,“对象实例创建者就是其使用者”这样的假定并不总是安全的。换句话说,一个组件可能创建 SAXParser 实例,而另一组件(可能由另一开发人员编码)可能需要使用那个实例。由于这个原因,提供了一些方法来确定语法分析器的设置。执行此任务的两个方法是 isValidating() ,它通知调用程序:语法分析器将要、或不要执行“确认”,以及 isNamespaceAware() ,它返回一个指示,说明语法分析器可以或不可以处理 XML 文档中的名称空间。虽然这些方法能提供有关语法分析器可以执行功能的信息,但是无法更改这些特性。必须在语法分析器类厂级别执行该操作。

另外,有多种方法来请求对文档进行语法分析。除了只接受 File 和 SAX HandlerBase 实例,SAXParser 的 parse() 方法还能以 String 形式接受 SAX InputSource 、Java InputStream 或 URL,所有这些都要与 HandlerBase 实例一起提供。所以,不同类型的输入文档可以用不同方式的语法分析来处理。

最后,可以直接通过 SAXParser 的 getParser() 方法获得和使用下层的 SAX 语法分析器( org.xml.sax.Parser 的实例)。获得这个下层实例之后,就可以获得通常的 SAX 方法。下一个清单显示 SAXParser 类(这个 JAXP 中 SAX 语法分析的核心类)的各种使用示例。



清单 2. 使用 JAXP SAXParser
 //获得SAXP的一个实例
            SAXParser saxParser = saxFactory.newSAXParser();
            //查看是否支持 Validate 选项
            boolean isValidating = saxParser.isValidating();
            //查看是否支持 namespace 选项
            boolean isNamespaceAware = saxParser.isNamespaceAware();
            // 运用一个File 和一个SAX HandlerBase 的实例进行多种形式的语法分析
            saxParser.parse(new File(args[0]), myHandlerBaseInstance);
            // 运用一个 SAX InputSource实例 和一个 SAX HandlerBase 实例
            saxParser.parse(mySaxInputSource, myHandlerBaseInstance);
            //运用一个 InputStream 实例和一个SAX HandlerBase 实例
            saxParser.parse(myInputStream, myHandlerBaseInstance);
            // 运用一个 URI 和一个SAX HandlerBase 实例
            saxParser.parse("http://www.newInstance.com/xml/doc.xml", myHandlerBaseInstance);
            //获得底层的(封装)SAX 语法分析器
            org.xml.sax.Parser parser = saxParser.getParser();
            //利用底层的语法分析器
            parser.setContentHandler(myContentHandlerInstance);
            parser.setErrorHandler(myErrorHandlerInstance);
            parser.parse(new org.xml.sax.InputSource(args[0]));
            

目前为止,关于 SAX 已经讲了很多,但是还没有揭示任何不寻常或令人惊奇的东西。事实上,JAXP 的功能很少,特别是当 SAX 也牵涉进来时。这很好,因为有最少的功能性意味着代码可移植性更强,并可以由其他开发人员与任何与 SAX 兼容的 XML 语法分析器一起使用,无论是免费(通过开放源码,希望如此)还是通过商业途径。就是这样。在 JAXP 中使用 SAX 没有更多的东西。如果已经知道 SAX,那么现在已经掌握大约 98% 的内容。只需学习两个新类和两个 Java 异常,您就可以开始了。如果从没使用过 SAX,那也很简单,现在就可以开始。





回页首


处理 DOM

如果要休息以迎接 DOM 挑战,那么先别休息。在 JAXP 中使用 DOM 的过程与 SAX 几乎相同,所要做的全部只是更改两个类名和一个返回类型,这样就差不多了。如果理解 SAX 的工作原理和 DOM 是什么,则不会有任何问题。

DOM 和 SAX 的主要差异是它们的 API 结构。SAX 包含一个基于事件的回调函数集,而 DOM 有一个内存中的树状结构。换句话说,在 SAX 中,从不需要操作数据结构(除非开发人员手工创建)。因此,SAX 不提供修改 XML 文档的功能。而 DOM 正好提供这种类型的功能。 org.w3c.dom.Document 类表示 XML 文档,它由表示元素、属性和其它 XML 结构的 DOM 节点 组成。所以,JAXP 无需触发 SAX 回调,它只负责从语法分析返回一个 DOM Document 对象。

DOM 语法分析器类厂一瞥

基本理解 DOM 以及 DOM 和 SAX 的差异之后,就没什么好说的了。以下代码看起来与 SAX 代码类似。首先,获得 DocumentBuilderFactory (与 SAX 中的方式相同)。然后,配置类厂来处理确认和名称空间(与 SAX 中的方式相同)。下一步,从类厂中检索 DocumentBuilder (它与 SAXParser 类似)(与 SAX 中的方式相同. . . 啊,您都知道了)。然后,就可以进行语法分析了,产生的 DOM Document 对象传递给打印 DOM 树的方法。



清单 3. 使用文档构建器类厂
import java.io.File;
            import java.io.IOException;
            import java.io.OutputStreamWriter;
            import java.io.Writer;
            // JAXP
            import javax.xml.parsers.FactoryConfigurationError;
            import javax.xml.parsers.ParserConfigurationException;
            import javax.xml.parsers.DocumentBuilderFactory;
            import javax.xml.parsers.DocumentBuilder;
            // DOM
            import org.w3c.dom.Document;
            import org.w3c.dom.DocumentType;
            import org.w3c.dom.NamedNodeMap;
            import org.w3c.dom.Node;
            import org.w3c.dom.NodeList;
            public class TestDOMParsing {
            public static void main(String[] args) {
            try {
            if (args.length != 1) {
            System.err.println ("Usage: java TestDOMParsing [filename]");
            System.exit (1);
            }
            // 获得 Document Builder Factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            //打开确认选项,关掉名称空间敏感性选项。
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new File(args[0]));
            // 从DOM 数中打印文档,并加一初始空格
            printNode(doc, "");
            // 在这里也可以对 DOM 文档进行修改
            } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not support the requested features.");
            } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining Document Builder Factory.");
            } catch (Exception e) {
            e.printStackTrace();
            }
            }
            private static void printNode(Node node, String indent) {
            // 打印 DOM 树
            }
            

此代码中可能会出现两个不同的问题(与 JAXP 中的 SAX 类似): FactoryConfigurationErrorParserConfigurationException 。每一个的原因与 SAX 中的相同。不是实现类 ( FactoryConfigurationError ) 中有问题,就是语法分析器不支持请求的特性 ( ParserConfigurationException )。DOM 和 SAX 的唯一差异是:在 DOM 中,用 DocumentBuilderFactory 替代 SAXParserFactory ,用 DocumentBuilder 替代 SAXParser 。就这么简单!(可以 查看完整代码清单 ,该清单包括用于打印 DOM 树的方法。)

使用 DOM 语法分析器

有了 DOM 类厂之后,就可以获得 DocumentBuilder 实例。 DocumentBuilder 实例可以使用的方法与 SAX 的非常类似。主要差异是 parse() 的变种不需要 HandlerBase 类的实例。它们返回表示语法分析之后的 XML 文档的 DOM Document 实例。另一唯一不同之处是:为类似于 SAX 的功能提供了两个方法:用 SAX ErrorHandler 实现来处理语法分析时可能出现的问题的 setErrorHandler() ,和用 SAX EntityResolver 实现来处理实体解析的 setEntityResolver() 。如果不熟悉这些概念,则需要通过联机或在我的书中学习 SAX。以下清单显示使用这些方法的示例。



清单 4. 使用 JAXP DocumentBuilder
    //获得一个 DocumentBuilder 实例
            DocumentBuilder builder = builderFactory.newDocumentBuilder();
            //查看是否支持 Validate 选项
            boolean isValidating = builder.isValidating();
            //查看是否支持 namespace 选项
            boolean isNamespaceAware = builder.isNamespaceAware();
            // 设置一个 SAX ErrorHandler
            builder.setErrorHandler(myErrorHandlerImpl);
            // 设置一个 SAX EntityResolver
            builder.setEntityResolver(myEntityResolverImpl);
            // 运用多种方法对 file 进行语法分析
            Document doc = builder.parse(new File(args[0]));
            // 运用 SAX InputSource
            Document doc = builder.parse(mySaxInputSource);
            // 运用 InputStream
            Document doc = builder.parse(myInputStream, myHandlerBaseInstance);
            // 运用 URI
            Document doc = builder.parse("http://www.newInstance.com/xml/doc.xml");
            

是不是感到 DOM 这一节有些令人厌烦?有这种想法的不止您一个,写 DOM 代码有些令人厌烦是因为它是直接取得所学的 SAX 知识,然后将其用于 DOM。因此,和朋友、同事打赌吧,说使用 JAXP 只是小菜一碟。





回页首


更改语法分析器

最后要探讨的主题是 JAXP 轻易更改类厂类使用的语法分析器的能力。更改 JAXP 使用的语法分析器实际意味着更改 类厂,因为所有 SAXParserDocumentBuilder 实例都来自这些类厂。既然确定装入哪个语法分析器的是类厂,因此,必须更改类厂。可以通过设置 Java 系统属性 javax.xml.parsers.SAXParserFactory 来更改要使用的 SAXParserFactory 接口实现。如果没有定义该属性,则返回缺省实现(供应商指定的任何语法分析器)。相同原理适用于 DocumentBuilderFactory 实现。在这种情况下,将查询 javax.xml.parsers.DocumentBuilderFactory 系统属性。就这么简单,我们已经学完了!这就是 SAXP 的全部:提供到 SAX 的挂钩,提供到 DOM 的挂钩,并允许轻易更改语法分析器。





回页首


结束语

如您所见,没多少复杂的东西。更改系统属性,通过类厂、而不是语法分析器或构建器来设置“确认”,以及弄清楚JAXP实际上不是人们通常所认为的那样,这些是使用 JAXP 的最困难部分。除了没有 SAX 2.0 和 DOM第二层规范支持之外,JAXP 在两个流行的 Java 和 XML API 之上提供一个有帮助的可插入层。它使代码独立于供应商,并允许不编译语法分析代码而更改语法分析器。那么,从 Sun、Apache XML 或其它方便之处下载 JAXP,并使用它吧!继续关注 JAXP 1.1,并增加对 SAX 2 和 DOM 2、XSLT 及更多内容的支持。您将在这里获得第一手新闻,所以,请关注 developerWorks



参考资料



关于作者

 

Brett McLaughlin 是 Lutris Technologies 的 Enhydra 策略顾问,他致力于研究分布式系统体系结构。他是 Java 和 XML (O'Reilly) 的作者。他还参与了诸如 Java Servlet、Enterprise JavaBeans 技术、XML 和B2B应用程序等技术的研究。最近,他与 Jason Hunter 一起创建了 JDOM 项目,该项目为在 Java 应用程序中使用 XML 提供了一个简单的 API。他还是 Apache Cocoon 项目、EJBoss EJB 服务器的主要开发人员,并且是 Apache Turbine 项目的共同创始人。Brett 目前在专家组从事 JAXP 1.1 的规范和发行。可以通过 brett@newInstance.com 与他联系。

posted @ 2007-07-06 12:40 和田雨 阅读(461) | 评论 (0)编辑 收藏

     摘要: 版权所有:(xiaodaoxiaodao)蓝小刀    xiaodaoxiaodao@gmail.com http://www.blogjava.net/xiaodaoxiaodao/articles/103469.html        转载请注明来源/作者 Java Web Start 入门基础教程 &nbs...  阅读全文
posted @ 2007-07-06 11:34 和田雨 阅读(295) | 评论 (0)编辑 收藏

 1. 介绍

    1)DOM(JAXP Crimson解析器)

    DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作。由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的。DOM以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航,而不是像SAX那样是一次性的处理。DOM使用起来也要简单得多。

    2)SAX

    SAX处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX还比它的替代者DOM快许多。

    选择DOM还是选择SAX? 对于需要自己编写代码来处理XML文档的开发人员来说, 选择DOM还是SAX解析模型是一个非常重要的设计决策。 DOM采用建立树形结构的方式访问XML文档,而SAX采用的事件模型。

    DOM解析器把XML文档转化为一个包含其内容的树,并可以对树进行遍历。用DOM解析模型的优点是编程容易,开发人员只需要调用建树的指令,然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用DOM解析器的时候需要处理整个XML文档,所以对性能和内存的要求比较高,尤其是遇到很大的XML文件的时候。由于它的遍历能力,DOM解析器常用于XML文档需要频繁的改变的服务中。

    SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag.特别是当开发人员只需要处理文档中所包含的部分数据时,SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。

    3)JDOM http://www.jdom.org

    JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快。由于是第一个Java特定模型,JDOM一直得到大力推广和促进。正在考虑通过“Java规范请求JSR-102”将它最终用作“Java标准扩展”。从2000年初就已经开始了JDOM开发。

    JDOM与DOM主要有两方面不同。首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。

    JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%)。JDOM对于大多数Java/XML应用程序来说当然是有用的,并且大多数开发者发现API比DOM容易理解得多。JDOM还包括对程序行为的相当广泛检查以防止用户做任何在XML中无意义的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作。

    JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。JDOM是在Apache许可证变体下发布的开放源码。

    4)DOM4J http://dom4j.sourceforge.net

    虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。从2000下半年开始,它就一直处于开发之中。

    为支持所有这些功能,DOM4J使用接口和抽象基本类方法。DOM4J大量使用了API中的Collections类,但是在许多情况下,它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。

    在添加灵活性、XPath集成和对大文档处理的目标时,DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。

    DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,特别值得一提的是连Sun的JAXM也在用DOM4J.

    2…… 比较

    1)DOM4J性能最好,连Sun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.

    2)JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。

    3)SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。

3. 四种xml操作方式的基本使用方法

xml
文件:


?xml version="1.0" encoding="GB2312"?
RESULT

VALUE

   NOA1234/NO

   ADDR>四川省XXXXXXXXX号</ADDR

/VALUE

VALUE

   NOB1234/NO

   <ADDR>四川省XXXXXXXX组</ADDR

/VALUE

/RESULT


1
DOM

import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;

public class MyXMLReader{
 
public static void main(String arge[]){

  
long lasting =System.currentTimeMillis();
  
try{
   
File f=new File("data_10k.xml");
   
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
   
DocumentBuilder builder=factory.newDocumentBuilder();
   
Document doc = builder.parse(f);
   
NodeList nl = doc.getElementsByTagName("VALUE");
   for (int i=0;i
nl.getLength();i++){
    System.out.print("车牌号码
:" + doc.getElementsByTagName("NO").item(i).getFirstChild().getNodeValue());
    System.out.println("车主地址
:" + doc.getElementsByTagName("ADDR").item(i).getFirstChild().getNodeValue());
   
}
  
}catch(Exception e){
   
e.printStackTrace();
}

2
SAX

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;

public class MyXMLReader extends DefaultHandler {

 
java.util.Stack tags = new java.util.Stack();
 
public MyXMLReader() {
  
super();
}

 
public static void main(String args[]) {
  
long lasting = System.currentTimeMillis();
  
try {
   
SAXParserFactory sf = SAXParserFactory.newInstance();
   
SAXParser sp = sf.newSAXParser();
   
MyXMLReader reader = new MyXMLReader();
   
sp.parse(new InputSource("data_10k.xml"), reader);
  
} catch (Exception e) {
   
e.printStackTrace();
  
}

  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + "毫秒
");}
  
public void characters(char ch[], int start, int length) throws SAXException {
  
String tag = (String) tags.peek();
  
if (tag.equals("NO")) {
   System.out.print("车牌号码:
" + new String(ch, start, length));
}
if (tag.equals("ADDR")) {
  System.out.println("地址
:" + new String(ch, start, length));
}
}

  
public void startElement(String uri,String localName,String qName,Attributes attrs) {
  
tags.push(qName);}
}

3
JDOM

import java.io.*;
import java.util.*;
import org.jdom.*;
import org.jdom.input.*;

public class MyXMLReader {

 
public static void main(String arge[]) {
  
long lasting = System.currentTimeMillis();
  
try {
   
SAXBuilder builder = new SAXBuilder();
   
Document doc = builder.build(new File("data_10k.xml"));
   
Element foo = doc.getRootElement();
   
List allChildren = foo.getChildren();
   for(int i=0;i
allChildren.size();i++) {
    System.out.print("车牌号码
:" + ((Element)allChildren.get(i)).getChild("NO").getText());
    System.out.println("车主地址
:" + ((Element)allChildren.get(i)).getChild("ADDR").getText());
   
}
  
} catch (Exception e) {
   
e.printStackTrace();
}

}

4
DOM4J

import java.io.*;
import java.util.*;
import org.dom4j.*;
import org.dom4j.io.*;

public class MyXMLReader {

 
public static void main(String arge[]) {
  
long lasting = System.currentTimeMillis();
  
try {
   
File f = new File("data_10k.xml");
   
SAXReader reader = new SAXReader();
   
Document doc = reader.read(f);
   
Element root = doc.getRootElement();
   
Element foo;
   
for (Iterator i = root.elementIterator("VALUE"); i.hasNext() {
    
foo = (Element) i.next();
    System.out.print("车牌号码
:" + foo.elementText("NO"));
    System.out.println("车主地址
:" + foo.elementText("ADDR"));
   
}
  
} catch (Exception e) {
   
e.printStackTrace();
}
)

posted @ 2007-07-06 11:31 和田雨 阅读(264) | 评论 (0)编辑 收藏

Sysdeo/SQLI Eclipse Tomcat Launcher plugin

在Eclipse方面使用Tomcat的插件

 

 

Plugin features

  • Starting and stopping Tomcat 4.x, 5.x and 6.x

  • Registering Tomcat process to Eclipse debugger

  • Creating a WAR project (wizard can update server.xml file)

  • Adding Java Projects to Tomcat classpath

  • Setting Tomcat JVM parameters, classpath and bootclasspath

  • Exporting a Tomcat project to a WAR File

  • Capability to use a custom Tomcat classloader to load classes in several java projects at the same classloader level than classes in a Tomcat project, see readmeDevLoader.html (Thanks Martin Kahr)

 

Support and contributions

Contact plugintomcat@sysdeo.fr

 

Download

This plugin is free and open-source

Version File Date Comment

3.2.1

10 May 2007

Works with Eclipse 3.1, 3.2 and 3.3M7
Fix a problem with HTTPS

3.2

13 April 2007

Works with Eclipse 3.1, 3.2 and 3.3M6
Tomcat 6 support added

3.2 beta3

20 November 2006

Works with Eclipse 3.2 and Eclipse 3.1
Tomcat 6 support added

3.2 beta2

25 October 2006

Works with Eclipse 3.2 and Eclipse 3.1
Tomcat 6 support added

3.1

15 September 2005

Works with Eclipse 3.0 and Eclipse 3.1 (fix a problem with Eclipse 3.1M6)

3.0

27 July 2004

Works with Eclipse 3.0 and Eclipse 2.1

2.2.1

1er August 2003

Works with Eclipse 2.1 and Eclipse 3.0 M2


Tomcat 4.x patch for JSP debugging (Thanks to Johan Compagner)

Tomcat Version File Date

4.1.24

1 April 2003

To install this patch, unzip the file in <%TOMCAT_HOME%>/classes for tomcat 4.0x, or in <%TOMCAT_HOME%>/common/classes for Tomcat 4.1.x

 

Installation

  • This plugin does not contain Tomcat.
    (Download and install Tomcat before using this plugin).
    This is a design choice not to include Tomcat in the plugin distribution, this way the same plugin version can works with any Tomcat version.

  • Download tomcatPluginVxxx.zip

  • Unzip it in your_Eclipse_Home/plugins

  • Plugin activation for Eclipse 3.x :
    - launch eclipse once using this option : -clean
    - if Tomcat icons are not shown in toolbar : select menu 'Window>Customize Perspective...>Commands', and check 'Tomcat' in 'Available command groups'

  • Set Tomcat version and Tomcat home : Workbench -> Preferences, select Tomcat and set Tomcat version and Tomcat home (Tomcat version and Tomcat home are the only required fields, other settings are there for advanced configuration).

  • This plugin launches Tomcat using the default JRE checked in Eclipe preferences window.
    To set a JDK as default JRE for Eclipse open the preference window : Window -> Preferences -> Java -> Installed JREs.
    This JRE must be a JDK (This is a Tomcat prerequisite).

  • The plugin sets itself Tomcat classpath and bootclasspath. Use Preferences -> Tomcat ->JVM Settings, only if you need specific settings.

 

Documentation and tutorials

Official documation (french) : http://www.eclipsetotale.com/articles/tomcat/tomcatPluginDocFR.html

Tutorials (english) :

 

Troubleshooting

  • In some case, despite it is correctly unzipped in Eclipse 3 'plugins' directory, the plugin is not loaded : run Eclipse with the -clean option to solve the problem.

  • ClassNotFound when using DevLoader and launching Tomcat 5.5.12 and above
    Workaround : set context definition mode (Preferences->Tomcat) to 'Context files'.
    Explanations :
    This problem is due to a change in Tomcat 5.5.12 and above (see http://issues.apache.org/bugzilla/show_bug.cgi?id=37302 ,
    We hope this bug will be fix but it seems that Tomcat developers don't care about it because it happens when a context containing its own loader is defined in server.xml file and since Tomcat 5 defining context in server.xml is not recommended)

  • ClassNotFoundException on javac/Main when accessing JSP : Tomcat is started with a JRE instead of a JDK.

  • If you have a problem with the plugin check eclipse log file (<%Your_Workspace_dir%>/.metadata/.log)

 

If you have any problem with this plugin send an email to plugintomcat@sysdeo.fr .
(Select this button : Preferences->Tomcat->JVM Settings->Dump configuration to .log file, and include in your email the corresponding lines of your .log file, it is in <%Your_Workspace_dir%>/.metadata)

posted @ 2007-07-05 16:28 和田雨 阅读(4263) | 评论 (0)编辑 收藏

这个插件是用来方面的使用 log4j组件的,非常好用,推荐!

Log4E is an Eclipse Plugin which helps you to use your logger easily in Java Projects.
The Plugin Log4E is not bound to any special logging framework. Thus you might be able to adapt to your own logger by defining your own templates using the preferences. It has active support for Log4j, Commons Logging and JDK 1.4 logging.

Use the context menu of a java file or the context menu of an editor and get to the submenu "Log4". Notice that the editor submenu provides additional tasks.

If you like this plugin, please rate it at

Download

Download the Plugin here


Overview

The following tasks can be invoked either on project, sourcefolder, package, class or method level.

Logger Declaration

 

    Inserting import statement and logger declaration of your favourite logger in your sourcecode automatically.
    Kind of logger and imports are configurable in the Preferences.

Logger Insertions

 

    Automate insertions of logger statements at certain method entries: begin of method, exit of method (before every return statement and at the end of method) and in catch blocks.
    Furthermore you can add a logger statement at any position you like supported by a wizard.
    logger levels and much more is configurable in the Preferences:Positions (Statements and Positions).

Substitution of System out's

 

    Replacement of System out's, System.err's and e.printTrackTrace Statements.
    Logger levels are configurable in the Preferences:Replacement.

Logger Modification

 

    Note that the modification task could delete a logger message under certain circumstances! Please read the Logger Modification:Algorithm section before using it.

    Modification of already exisiting logger statements: Replacing logger message with standard information, surrounding log statements with isLevelEnabled() if clauses and more.



Don't hesitate to request for new features or help at log4e@jayefem.de

posted @ 2007-07-05 16:26 和田雨 阅读(548) | 评论 (0)编辑 收藏

仅列出标题
共9页: 上一页 1 2 3 4 5 6 7 8 9 下一页