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 和田雨 閱讀(191) | 評論 (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 和田雨 閱讀(269) | 評論 (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 和田雨 閱讀(234) | 評論 (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 和田雨 閱讀(270) | 評論 (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 和田雨 閱讀(204) | 評論 (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 和田雨 閱讀(480) | 評論 (0)編輯 收藏

               摘要: 版權所有:(xiaodaoxiaodao)藍小刀    xiaodaoxiaodao@gmail.com http://www.aygfsteel.com/xiaodaoxiaodao/articles/103469.html        轉載請注明來源/作者 Java Web Start 入門基礎教程 &nbs...  閱讀全文
          posted @ 2007-07-06 11:34 和田雨 閱讀(323) | 評論 (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 和田雨 閱讀(284) | 評論 (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 和田雨 閱讀(4295) | 評論 (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 和田雨 閱讀(568) | 評論 (0)編輯 收藏

          僅列出標題
          共9頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 
          主站蜘蛛池模板: 舒城县| 斗六市| 乡宁县| 西安市| 西吉县| 仁布县| 黄山市| 绵竹市| 公安县| 共和县| 苏尼特右旗| 二手房| 沈阳市| 永丰县| 南乐县| 达州市| 邻水| 淳安县| 新干县| 南通市| 拜泉县| 集贤县| 措美县| 盐边县| 于都县| 城市| 且末县| 揭东县| 吉林省| 阿合奇县| 都兰县| 乐陵市| 塘沽区| 钦州市| 南投县| 安溪县| 郸城县| 民勤县| 静宁县| 永宁县| 子长县|