用 XDoclet 減少程式碼膨脹

級別: 入門

Sing Li, 作者

2004 年 11 月 05 日

開放原始程式碼的 XDoclet 程式碼產生引擎,是許多領先的 Java 框架不可缺少的組成部分,常常被用作屬性導向的程式設計和持續整合的引擎。但是 XDoclet 還有一些不太惹人注目的地方:對初級開發人員來說,它太難掌握、太難精通。在這篇文章中,熱門作者 Sing Li 以 XDoclet 為物件,揭示了其內部簡單卻優雅的設計,使您能夠理解這項技術,並將它應用在實務當中。

XDoclet 能夠很容易成為您的 Java 程式設計工具箱中的一個更加通用的跨技術程式碼產生工具。不幸的是,開發人員經常忽視 XDoclet 的一般用途,只有將它捆綁在大型開發框架或者 IDE 中,作為其中的一個隱藏元素時,才會用到它。人們常常認為很難將 XDoclet 應用在制定解決方案上。這篇文章的目的就是要消除這個迷惑,把 XDoclet 從常見的複雜陷阱中解脫出來,並向您展示了如何能夠利用這個程式碼產生引擎。

我會用一個實際的例子示範 XDoclet 的用途,該例子將接收一個 POJO(plain old Java object),並用 XDoclet 產生完整 Web 應用程式的全部檔案,這些檔案是把資料登錄關聯資料庫所必需的。該範例使用了 XDoclet 的自定義範本程式碼產生功能,以及它對 Hibernate 物件關係對應工具、Struct Web 應用程式框架和應用程式伺服器的內部支援。(請參閱參考資料)。

智慧程式碼產生器

XDoclet 的核心功能是根據以下組合來產生程式碼的(或者產生其他設定/資料檔案):

  • 進行特殊標記的 Java 原始檔案。
  • 預先定義的範本。

與其他基於範本的程式碼產生技術(例如 Velocity;請參閱參考資料)相比,XDoclet 具有以下獨特優勢:

  • XDoclet 與 Apache Ant(請參閱參考資料)緊密整合,從而提供了高度自動化的操作。

  • 把控制程式碼產生和範本處理的 XDoclet 標籤作為內嵌注釋嵌入到 Java 原始程式碼檔中。這消除了同步多個相關檔案和控制檔的需要。

  • XDoclet 的內建 Java 解析器使用它對 Java 程式碼結構的深入理解,為輸入的 Java 程式碼建立內部結構模組 。該結構模組又經常被叫作 中繼資料(metadata),因為它包含與關聯程式碼有關的資料。

  • XDoclet 的範本產生邏輯擁有對輸入 Java 程式碼的內部結構模組的完全存取權。

接下來,我將進一步研究 XDoclet 是如何工作的,以幫助您理解這些特性。

XDoclet 操作

圖 1 顯示了 XDoclet 要求的輸入和產生的輸出。


圖 1. XDoclet 黑盒子
圖 1

您可以看到,包含嵌入式 XDoclet 標籤的 Java 原始程式碼是系統的輸入。在 Apache Ant 的驅動下,XDoclet 處理輸入的程式碼,產生的輸出文字檔案可以是 Java 原始程式碼、HTML 頁面、XML 檔等。為了處理輸入,XDoclet 需要使用範本(保存在 .xdt 檔中)和標籤處理器(用 Java 編碼)。XDoclet 把範本和標籤處理器封裝成“模組”,不同的“模組”處理不同領域的問題。

XDoclet 產生的結構模組

XDoclet 對包含嵌入式 XDoclet 標籤的輸入 Java 原始程式碼進行解析,並為程式碼建立非常詳細的結構模組。結構模組中的每個元素都代表原始程式碼中的一個 Java 結構。圖 2 顯示的結構模組,揭示了 XDoclet 追蹤的程式碼構造和關係。


圖 2. XDoclet 的解析的 Java 原始程式碼的內部結構模組
圖 2

圖 2 中的結構模組追蹤類別、介面、方法之類的程式碼構造(模組元素)。該模組還追蹤元素之間的關係,例如繼承和介面實作。以內嵌注釋的形式嵌入在原始程式碼中的 XDoclet 標籤被解析為模組元素的屬性,並被追蹤。




深入 XDoclet

圖 3 顯示了 XDoclet 的內部結構,揭示了使其執行的功能區塊。


圖 3. XDoclet 內部的功能區塊
圖 3

如圖 3 所示,Apache Ant 在執行的時候控制著 XDoclet 的設定和操作。XDoclet 解析輸入的 Java 原始程式碼,並在記憶體中產生結構模組。範本引擎透過處理一組範本和標籤處理器,產生輸出檔。範本和標籤處理器可以是內建的,也可以是制定的。在程式碼產生期間,範本和標籤處理器擁有對結構模組的完全存取。

XDoclet 虛假的複雜性

XDoclet 實質上就是一個通用的 Javadoc 引擎(請參閱側欄,通用的 Javadoc 引擎)。那麼,是什麼讓它看起來這麼複雜呢?答案在於:XDoclet 幾乎從未被單獨討論過,而總是藏在其他許多複雜的技術中。圖 4 顯示了圍繞在 XDoclet 周圍的複雜性迷霧(請參閱側欄為什麼 XDoclet 看起來比實際的要複雜得多)。


圖 4. XDoclet 的複雜耦合
Figure 4

在圖 4 中,您可以看到 XDoclet 與以下內容是緊密相關的:

  • Apache Ant,它控制著 XDoclet 的操作。XDoclet 是作為一組 Ant 任務存在的,沒有 Ant 則不能執行。
  • 與產生文件關聯的具體問題領域的一些細節。

XDoclet 本身卻是驚人地簡單,正如下面範例中的工作程式碼所示的那樣。




使用 XDoclet

現在,您可以透過研究我向您提供的資料入口應用程式範例,來觀察 XDoclet 的實際工作。(要下載這個範例中使用的 Java 程式碼、XDoclet 範本和 Ant 腳本,請點選本文頂部或底部的 Code 圖示,或者請參閱下載部分。)我們將從檢查清單 1 所示的 Java 程式碼開始,這部分程式碼表示了一個客戶的位址。該位址被編碼成 JavaBean 元件,其中的 XDoclet 標籤是以黑體字形顯示的:


清單 1. 用 XDoclet 標籤標記的 AddressBean.java 原始檔案

package com.ibm.dw.beans;


import java.io.Serializable;

/**
* @dw.genStrutsAction action="/addAddress.do"

* @hibernate.class table="ADDRESS"

*/

public class AddressBean implements Serializable {


private String streetNumber = "";

private String street = "";

private String city = "";

private String country = "";

private String postalCode = "";

private long id = 0;



public AddressBean() {

}

/**

* @dw.genStruts formlabel="Street Number"

* @hibernate.property length="10"

*/

public String getStreetNumber() {

   return streetNumber;

}


public void setStreetNumber(String inpStreetNumber) {

   streetNumber = inpStreetNumber;

}


/**

* @dw.genStruts formlabel="Street"

* @hibernate.property  length="40"

*/

public String getStreet() {

    return street;

}


public void setStreet(String inpStreet) {

   street = inpStreet;

}


...... more Address bean properties ......


/**

* @hibernate.id generator-class="native"

*/

public long getId(  )

{

        return id;

}


public void setId(long inId) {

   id = inId;

}


}

在清單 1 中,需要注意的是,要把 XDoclet 標籤嵌入到注釋中,緊放在相關程式碼元素(例如欄位、方法、介面或類別)的前面。在解析原始程式碼時,XDoclet 會為每個標籤建立一個屬性,並將該屬性附加到結構模組的程式碼元素上。現在,請注意 @dw.genStruts 標籤,因為這是在本例中將用到的第一個範本。

產生另外一個 Java 類別

對於本例,您需要產生新的 Java 類別的程式碼 —— 一個 Struts 表單 bean。Struts 會用這個 bean 保存並傳輸用戶輸入。bean 必須以 bean 屬性的形式包含所有資料欄位,而且它必須是 org.apache.struts.action.ActionForm 的子類別。

為了產生表單 bean 的程式碼,您要根據清單 2 所示的虛擬程式碼產生 XDoclet 範本。括弧中的黑體字代表控制流邏輯和您要進行替換的文本。請注意範本是如何從已解析的 Java 原始程式碼檔的結構模組中提取資訊的:


清單 2. 建立 AddressBeanForm.java Struts 表單 bean 程式碼的虛擬程式碼範本

package {package name of source class};

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.upload.FormFile;


/**

 * Form Bean class for {name of source class};.

 *
 * @struts.form name="{name of source class}Form"

 */
public class {name of source class}Form extends ActionForm {


{loop through all the methods in the source class}

   {if the method is a JavaBean "getter" method}

      {if the method has been marked with the @dw.genStruts tag }


	private {return type of method}

				{name of the JavaBean property}; 


	public {return type of method}

				{name of the getter method for this property}(){

		return {name of JavaBean property};

	}


	public void {name of the setter method for this property}(

	                            {return type of method} value) {

            {name of the JavaBean property} = value; 
     		
	}

       {end of if @dw.genStruts}

    {end of if JavaBean getter}

{end of loop}
			

用 XDoclet 建立 XDoclet 標籤

請注意在清單 2 的範本中產生的 XDoclet @struts.form 標籤。您可以用 XDoclet 在 Java 原始程式碼中產生 XDoclet 標籤,XDoclet 會在後面的操作中再次處理這些標籤。在範例後面建立 structs-config.xml 的時候,XDoclet 會使用 @struts.form 標籤。



在清單 2 的迴圈中的程式碼產生了一個欄位宣告和一個存取器方法,還為輸入原始程式碼中每個用 @dw.genStruts 標記的存取器方法產生了一個設定器方法。

清單 2 使用了易於理解的虛擬程式碼來表示範本替換標籤。實際的 XDoclet 範本標籤則相當繁瑣。清單 3 顯示了 genformbean.xdt 範本(所有的 XDoclet 範本都保存在 .xdt 文件中)。我已經用黑體字強調了 XDoclet 範本標籤,以方便在虛擬程式碼中對其進行引用。


清單 3. 建立 Structs 表單 bean Java 程式碼的實際 XDoclet 範本程式碼

package <XDtPackage:packageName/>;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.upload.FormFile;


/**

 * Form Bean class for <XDtClass:className/>.

 *

 * @struts.form name="<XDtClass:className/>Form"

 */

public class <XDtClass:className/>Form extends ActionForm {


<XDtMethod:forAllMethods>

   <XDtMethod:ifIsGetter>

      <XDtMethod:ifHasMethodTag tagName="dw.genStruts">



	private <XDtMethod:methodType/> <XDtMethod:propertyName/>; 


	public <XDtMethod:methodType/> <XDtMethod:getterMethod/>(){

		return <XDtMethod:propertyName/>;

	}


	public void <XDtMethod:setterMethod/>(<XDtMethod:methodType/> value)
 {
            <XDtMethod:propertyName/> = value;  
    		
	}

     </XDtMethod:ifHasMethodTag>

   </XDtMethod:ifIsGetter>

</XDtMethod:forAllMethods>
			

熟悉 Ant 腳本編寫

Ant 腳本 build.xml(我已經隨本文的範例程式碼提供了這個檔,請參閱 下載部分 找到程式碼的連結)定義了範例應用程式使用的全部必要 Ant 目標。如果您想修改腳本,就需要熟悉 Ant。請參閱 參考資料 瞭解 Ant 的更多資訊。

您可以參考 XDoclet 的“範本語言”文件,尋找 XDoclet 所有可用標籤的列表(請參閱參考資料)。

要執行用於 AddressBean.java 原始檔案的範本,請使用以下 Ant 命令行:

ant -Dbase.class.java=Address genstruts

這個命令可以執行制定 Ant 目標(請參閱側欄熟悉 Ant 腳本編寫)來處理 genbeanform.xdt 範本。XDoclet 提供的 Ant 任務叫作 xdoclet.DocletTask,它被用來執行範本檔。如果您對 Ant 的細節感興趣,請參閱範例程式碼中的 build.xml 檔,以瞭解更多資訊。

在 XDoclet 處理範本的時候,它在名為 generated 的子目錄下產生一個 AddressBeanForm.java 文件。清單 4 顯示了該檔,它包含範本處理期間替換的所有文本:


清單 4. XDoclet 產生的包含 Struts 表單 bean 的 Java 原始程式碼

package com.ibm.dw.beans;


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.upload.FormFile;


/**

 * Form Bean class for AddressBean.

 *

 * @struts.form name="AddressBeanForm"

 */

public class AddressBeanForm extends ActionForm {


	private java.lang.String streetNumber; 


	public java.lang.String getStreetNumber(){

		return streetNumber;

	}


	public void setStreetNumber(java.lang.String value) {

            streetNumber = value;   
   		
	}


	private java.lang.String	street; 


	public java.lang.String getStreet(){

		return street;

	}


	public void setStreet(java.lang.String value) {

            street = value;   
   		
	}

   ...... more bean properties .....


}

為資料表單輸入產生 JSP 頁面

您可以用相同的 AddressBean.java 原始檔案,但是用 genformjsp.xdt 範本產生資料入口表單 JSP 頁面。清單 5 顯示了 genformjsp.xdt:


清單 5. 使用 Struts 標籤庫產生 JSP 頁面來顯示 HTML 表單的 XDoclet 範本

<%@ page language="java" %>

<%@ taglib  uri="/WEB-INF/struts-html.tld" prefix="html" %>


<html:html>

<head>

</head>


<body bgcolor="white">


<html:errors/>


<html:form action="<XDtClass:classTagValue tagName='dw.genStrutsAction'  paramName='action' /> >"

<table border="0" width="100%">


<XDtMethod:forAllMethods>

   <XDtMethod:ifIsGetter>

      <XDtMethod:ifHasMethodTag tagName="dw.genStruts" >



    <tr>

        <th align="right">

           <XDtMethod:methodTagValue 
                   tagName="dw.genStruts" paramName="formlabel"/>

        </th>

        <td align="left">

            <html:text  property="<XDtMethod:propertyName/>"

              size="<XDtMethod:ifHasMethodTag tagName="hibernate.property" >

              <XDtMethod:methodTagValue tagName="hibernate.property" paramName="length"/>

              </XDtMethod:ifHasMethodTag>"/>

        </td>

    </tr>


     </XDtMethod:ifHasMethodTag>

   </XDtMethod:ifIsGetter>

</XDtMethod:forAllMethods>


    <tr>

        <td align="right">

            <html:submit>

                Submit

            </html:submit>

        </td>

        <td align="left">

            <html:reset>

              Reset

            </html:reset>

        </td>

    </tr>

</table>


</html:form>

</body>

</html:html>

請注意,程式碼中用 <XDt:methodTagValue> 取得原始 AddressBean.java 程式碼中在 XDoclet 標籤中指定的屬性值。

當您執行 genstruts Ant 目標的時候,也會處理清單 5 顯示的 genformjsp.xdt 範本。您可以在 generated 子目錄中找到產生的 AddressBeanForm.jsp 檔案,檢查檔案內容,可以看到要對範本進行的替換。

試驗範例,但是首先...

要試驗本文範例中的範本化程式碼產生,您需要安裝並測試好 Ant 和 XDoclet。本文中的程式碼全都基於 Ant 1.5.4 和 XDoclet 1.2.1。要產生所有的 artifact ,並試驗產生的 Web 應用程式,還需要安裝 Hibernate,Struts,以及應用程式伺服器,還需要能夠存取關聯資料庫。範例基於 Hibernate 2.1.4,Struts 1.1,以及 Servlet 2.3。請參閱參考資料 瞭解有關這些技術的更多信息。請一定要閱讀我隨原始程式碼一起提供的 README 檔。

產生其他 artifact

您可以用 XDoclet 產生任意基於文本的輸出。我向您展示的例子使用 XDoclet 產生了 Java 程式碼、JSP 頁面、XML 檔、設定檔以及其他更多輸出。它從一個簡單的用 XDoclet 進行標記的 Java 原始檔案 AddressBean.java,建立了一個完整的資料入口 Web 應用程式。為了做到這一點,它執行了 XDoclet 的內建範本(位於 JAR 檔中,稱為模組),從而產生:

  • Struts 設定和支援檔。
  • Hibernate 設定和支援檔。
  • Web 應用程式的部署描述符(web.xml)。

表 1 顯示了為範例應用程式產生的所有檔案(通常稱為 artifact ):


表 1. XDoclet 為 AddressBean.java 產生的 artifact
產生的 artifact 說明 位置
AddressBeanForm.javaJava 原始檔案,包含表單 bean 類別,在 Struts 的表單處理中使用generated 目錄
AddressBeanForm.jspJSP 表單,用 Struts 標籤庫接受用戶地址輸入jsp 目錄
AddressBeanAction.javaStruts 動作類別,接受輸入值,用 Hibernate 把值保存到關聯資料庫generated 目錄
AddressBean.hbm.xmlHibernate 對應檔,在 AddressBean Java 物件和資料庫的關聯式 ADDRESS 表之間進行對應web/classes 目錄
dwschema.sqlRDBMS 表的架構,用來對 AddressBean 物件的實例進行持久化sql 目錄
hibernate.cfg.xmlHibernate 執行時的設定檔web/classes 目錄
web.xml產生的 Web 應用程式的部署描述符web 目錄
struts-config.xmlStruts 框架的設定檔web 目錄

在這篇文章中,您詳細瞭解了表 1 中所列的兩個 artifact 中的第一個 artifact 的產生,深入瞭解了產生它們的範本。 artifact AdddressBeanAction.java 則用類似的方法,利用叫作 genaction.xdt 的範本產生。XDoclet 具有內建範本和標籤處理器,可以產生表 1 中的其他 artifact 。

表 2 列出了每個產生的 artifact 對應的 Ant 目標和 Ant 任務。您可以執行表格中的每個 Ant 目標,產生對應的 artifact 。所有這些產生的 artifact ,再加上原始和 AddressBean.java,共同構成了範例 Web 應用程式。您還會發現叫作 all 的預設 Ant 目標,它會為您做任何事,包括為應用程式建立 WAR(可以部署的 Web 歸檔)。在進行處理之前,一定要閱讀程式碼發佈套件中的 README.txt 文件。


表 2. 對應於產生 artifact 的 Ant 目錄和 Ant 任務


Ant 目標 Ant 任務 artifact
genstruts xdoclet.DocletTask AddressBeanForm.java
genstruts xdoclet.DocletTask AddressBeanForm.jsp
genstruts xdoclet.DocletTask AddressBeanAction.java
generateHIB xdoclet.modules.hibernate.HibernateDocletTask AddressBean.hbm.xml
generateHIB xdoclet.modules.hibernate.HibernateDocletTask hibernate.cfg.xml
createDDL xdoclet.modules.hibernate.HibernateDocletTask dwschema.sql
generateDD xdoclet.modules.web.WebDocletTask web.xml
generateDD xdoclet.modules.web.WebDocletTask struts-config.xml



結論

XDoclet 是一個有用的、智慧的程式碼產生器,您可以用它自動進行許多日常的 Java 開發任務。不要被它表面的複雜所嚇退。隨著逐漸精通 XDoclet(以及與之相關的 Apache Ant),您會節約您寶貴的時間,並在未來的開發工作中,得到數倍的回報。





下載

名稱檔案大小下載方式
j-xdoclet-code.zip  FTP
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值