,,
(1.江南大學信息工程學院,江蘇 無錫 214122;2.中國船舶科學研究中心,江蘇 無錫 214082)
眾所周知,軟件開發人員和頁面設計人員往往因為一些重合的業務邏輯功能而交織在一起不能獨立工作,以致于頁面設計人員不能把精力集中在使用標簽創建網站上,而軟件開發人員也不能將精力完全集中在底層功能的實現上[1];此外JSP和Servlet 作為J2EE 平臺中生成動態WEB的技術,雖能產生相同的效果,但JSP在內容中嵌入了邏輯,從而降低了JSP頁面的可讀性、可維護性和可復用性,而Servlet在邏輯中嵌入了內容,以至于其邏輯處理混亂,可讀性差,同時也給表示內容的維護帶來了不利。雖然JavaBean的引入很好地解決了JSP和Servlet面臨的兩難處境,使J2EE平臺Web組件的開發很好地實現了內容和表示、業務邏輯的分離,但JavaBean只是可復用組件,無法獲取運行環境信息。基于以上問題,引入自定義標簽是一個很好的解決途徑,可以使Web組件開發中的表示和業務邏輯代碼相分離,大大增強了代碼的可讀性、可維護性和可復用性。
當一個含有自定義標簽的JSP頁面被編譯成servlet時,實際上標簽被轉化成了對標簽處理類的操作,即JSP引擎會根據頁面指令<%@ taglib>去訪問TLD的相關處理程序信息,調用它的構造器方法,啟動標簽處理程類,讀取其屬性和相應值。如果是第一次使用,則對每個屬性都調用set方法。然后JSP服務器調用處理程序的doStartTag(),再調用doEndTag()方法。最后在頁面結尾調用release ()方法,清理占用的所有資源。
因此定制標簽的首要組件就是要寫一個實現標簽功能的JAVA類,即標簽處理器。它必須實現Tag接口或BodyTag接口[2],但通常的做法是繼承TagSupport或BodyTagSupport類,具體使用什么接口或者抽象類取決于開發人員定義的方法所預期的生命周期。
其次寫一個.tld格式的標簽庫描述器組件。它定義了標簽庫的名稱、版本信息、描述信息各標簽的名稱、對應的標簽處理器,標簽的屬性等內容。
另外,還需要一個部署標簽庫的xml文檔,是用來注冊描述標簽庫相關信息的文檔,一般在WEB服務器啟動時被加載到內存中。WEB服務器依此來調度資源響應用戶的請求。
其通常實現的接口有:javax.servlet.jsp.tagext.Tag以及其擴展接口IterationTag和 BodyTag。為了簡化用戶開發通常在javax.servlet.jsp.tagext包中還提供了等一些支持類,方便標簽處理器實現眾多的方法。
通常標簽的信息存儲在一個用XML編寫的TLD文檔中。因為標簽庫是一些標簽的集合,它們之間有一定聯系,加之標簽處理器的方法有很多是容器回調方法,編寫完標簽后,只有向WEB容器提供關于標簽的信息,才能在用戶引用時,WEB容器知道怎樣調用相應的方法來處理客戶的請求[4]。因此將其相應的信息放在一個說明文件里有利于引用時的操作。
由于標簽庫使用TLD文件描述,因而部署標簽庫實際上就是要發布TLD文件,通常把它放在WebContent下的WEB-INFO文件夾里。在web.xml文件中提供標簽庫描述文件的路徑[4]。
本文結合的項目是ShipDB_Test系統的子系統,船舶性能試驗數據庫系統,其具體需求見圖1。
船舶試驗數字化平臺是一個基于SOA(Service-Oriented Architecture)架構的試驗數據服務平臺系統,它實現了多個實驗室的試驗系統的無縫集成。由于該服務平臺包含多個子系統,因此在項目的集成過程中,項目的登錄成了需要解決的焦點,既不能影響其它系統的登錄,還得在登錄后能實現原本的權限控制以及功能實現。因此,在平臺的開發過程中定義了一個標簽庫,實現的主要功能有:實現不同用戶登錄及操作的權限控制,與其他系統集成后的單點登錄(集成后,標簽中需要修改相應的參數)等。在此僅給出選擇不同的語言登錄標簽的實現過程。

圖1 船舶性能試驗數據庫系統結構
相應標簽處理程序的部分源碼如下:
public class EnglishTag extends TagSupport {
/** Instance Variables、Property
* Public Methods */
public int doStartTag() throws JspException {HttpServletRequest request=(HttpServletRequest) pageContext.getRequest(); getAttribute(org.apache.struts.Globals.MODULE_KEY);
HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
String path = new String();
path = request.getServletPath();
String html = “”;
html=“
StringBuffer results = new StringBuffer(html);
/* jspwriter是一個隱含對象,用于向jsp網頁輸出內容 */
JspWriter writer = pageContext.getOut();
try {
writer.print(results.toString());
} catch (IOException e) {
throw new JspException (messages.getMessage(“linkItem.io”, e.toString())); return (EVAL_BODY_INCLUDE);}
public int doEndTag() throws JspException {
JspWriter writer = pageContext.getOut();
try {
writer.print(“”);
} catch (IOException e) {
throw new JspException
(messages.getMessage(“link.io”, e.toString()));}
return (EVAL_PAGE);}
public void release() {
super.release();//釋放占用的所有資源
action = “Show”; }
}
選擇其他語言登錄的標簽處理器類和以上是很相似的,其不同點是 “language=eng&path”中value值的變化。
/*標頭信息 */
/*標簽庫定義信息 */
/*標簽庫信息*/
/*標簽屬性集*/
將rmc.tld文件放在WebContent下的WEB-INFO目錄下,接下來就可以在JSP文件中導入定制的標簽庫和定制的標簽了,操作的語法如下:
/*導入標簽庫*/
<%@ taglib uri=“/WEB-INF/rmc.tld” prefix=“rmc” %>
/*引用標簽*/
本文的創新之處在于以簡單明了的自定義標簽來實現原來項目中繁雜冗余的源碼功能。例如,在SOA開發中,多個系統集成而又不影響各個獨立系統的登錄的做法可以利用單點登錄的解決方案,即所有應用系統的“統一登錄”,用戶一次登錄即可訪問其有權訪問的系統資源,提高了企業的工作效率。通常的做法就是利用專門的工具來實現。而在本系統中采用標簽來實現如此繁瑣的功能,不僅使項目變得簡單易維護,也使得Web組件開發中的表示和業務邏輯相分離[5],提高了開發效率。
[1] David M geary.JSP高級開發與應用[M].賀民,譯.北京:科學出版社,2006.
[2] 戎 偉,張 雙.精通Struts-java流行服務器、框架、工具及整合的應用[M].北京:人民郵電出版社,2006.
[3] 張新曼.精通JSP—WEB開發技術與典型應用[M].北京:人民郵電出版社,2006.
[4] 邱 哲,王俊標,馬 斗.Struts Web設計與開發大全[M].北京:清華大學出版社,2006.
[5] 梁愛虎.基于服務總線的Struts+EJB+Web Service整合應用開發[M].北京:電子工業出版社,2006.