實(shí)現(xiàn)圖形JSF組件
很簡(jiǎn)單地構(gòu)建一個(gè)純HTML無(wú)法輕松實(shí)現(xiàn)的圖形Web應(yīng)用程序組件
作者:Marc Durocher
http://dev2dev.bea.com.cn/techdoc/wlworkshop/2005070704.html
開發(fā)人員認(rèn)為,如果有合適的工具來創(chuàng)建交互式Web界面,他們就能將時(shí)間集中在核心需求和定制上,并在規(guī)定時(shí)間內(nèi)及時(shí)得交付應(yīng)用程序。與其他技術(shù)如JavaServer Pages或Apache Struts 相比,JavaServer Faces (JSF)技術(shù)為創(chuàng)建交互式Web應(yīng)用程序帶來了很多便利。JSF在程序邏輯和GUI表示之間劃出一條清晰的界限,提高了對(duì)Web程序的維護(hù)能力,并為Web用戶界面組件的開發(fā)和重用提供了一個(gè)框架。

  如今,許多Web應(yīng)用程序開發(fā)人員都在轉(zhuǎn)而使用JSF,但是他們發(fā)現(xiàn),預(yù)先定制的JSF UI組件受到基本DHTML窗口部件的限制。監(jiān)管或業(yè)務(wù)流程監(jiān)控之類的高級(jí)應(yīng)用程序需要能與JSF框架兼容的高級(jí)可視化組件。JSF框架的標(biāo)準(zhǔn)化使它易于開發(fā)能夠重用的自定義Web GUI組件。另外,Web組件開發(fā)商現(xiàn)在能提供更復(fù)雜的組件,并承諾Web應(yīng)用程序開發(fā)人員能夠輕松地使用這些組件。此類JSF用戶界面組件必須集成并部署到JSF運(yùn)行時(shí)框架中去,并在其中完全展開,還必須在設(shè)計(jì)時(shí)很好地集成到提供JSF支持的IDE中去。

  盡管JSF帶來了標(biāo)準(zhǔn)用戶界面框架,但對(duì)于開發(fā)第一個(gè)自定義JSF組件而言,還是存在幾個(gè)缺陷和漏洞。讓我們看看怎樣創(chuàng)建一個(gè)純HTML無(wú)法輕松創(chuàng)建的圖形JSF組件。圖形JSF組件的特點(diǎn)不僅要求生成DHTML,而且還需要對(duì)圖像生成和客戶端交互提供補(bǔ)充支持。我們將以一個(gè)圖形組件的例子來闡述這些特點(diǎn)。該圖形組件能夠提供曲線圖,并為各種客戶端導(dǎo)航和交互提供便利。我們還會(huì)了解到將該圖形組件集成到JSF-enabled IDE中所需要的步驟。通過理解圖形組件的設(shè)計(jì)方法,您將會(huì)更好地理解如何實(shí)現(xiàn)JSF組件,而這應(yīng)該能使您開發(fā)出定制的JSF圖形組件。

什么是JSF?

  JSF是一種能夠簡(jiǎn)化Web應(yīng)用程序表示層結(jié)構(gòu)的標(biāo)準(zhǔn)服務(wù)器端框架。定義JSF框架的JSR 127(參見參考資料)帶有一個(gè)能提供基本UI組件(如輸入欄和按紐)的參考實(shí)現(xiàn)。您可以將可重用用戶界面組件集中起來創(chuàng)建Web頁(yè),將這些組件綁定到應(yīng)用數(shù)據(jù)源上,并用服務(wù)器端事件控制程序處理客戶端事件。根據(jù)說明書介紹,組件供應(yīng)商能編寫與JSF運(yùn)行時(shí)框架集成的組件,并將其集成到在設(shè)計(jì)時(shí)與JSF兼容的IDE中去。

  從很大程度上講,JSF組件同在HTML 2.0技術(shù)要求下可用的HTML組件和標(biāo)簽直接相符合。對(duì)許多Web應(yīng)用程序而言,這套相對(duì)簡(jiǎn)單的組件是夠用的。然而,許多應(yīng)用程序如監(jiān)管或監(jiān)控程序需要更復(fù)雜的數(shù)據(jù)顯示與交互,比如制表、制圖和映射。由于JSF組件在HTML中直接提交復(fù)雜圖形小部件的能力有限,所以設(shè)計(jì)這些高級(jí)組件的能力并不突出。解決方案要求服務(wù)器端組件向客戶傳輸圖像,卻會(huì)給自身帶來問題,因?yàn)樵诨綡TML圖像上進(jìn)行交互要受到限制。最后,使用JavaScript時(shí),必須能調(diào)用客戶端交互來使用戶能對(duì)數(shù)據(jù)進(jìn)行導(dǎo)航和交互。

  讓我們看看開發(fā)一個(gè)簡(jiǎn)單的、將CSS輸入HTML頁(yè)面的JSF組件需要哪些步驟。當(dāng)開發(fā)高級(jí)JSF圖形組件時(shí),這一簡(jiǎn)單組件的描述和代碼樣本會(huì)作為背景。圖1顯示了如何使用即將開發(fā)的組件,并顯示將要得到的結(jié)果。使用這種組件的好處是能夠通過改變某個(gè)JSF動(dòng)作的組件值,來改變整個(gè)頁(yè)面的外觀。

圖1:顯示了我們?nèi)绾问褂靡粋€(gè)非常簡(jiǎn)單的JSF組件將CSS輸入某個(gè)HTML頁(yè)面并得出結(jié)果。

開發(fā)組件

  JSF組件包含若干個(gè)Java類和配置文件。為創(chuàng)建一個(gè)自定義JSF組件,您需要開發(fā)一個(gè)擴(kuò)展JSF基本組件類的Java類;為默認(rèn)呈現(xiàn)軟件包開發(fā)呈現(xiàn)程序;開發(fā)一個(gè)將在JSP頁(yè)面中用于描述標(biāo)簽的Java類;編寫一個(gè)標(biāo)簽庫(kù)定義(TLD)文件;編寫JSF配置文件。讓我們更深入地了解這5個(gè)步驟。

  開發(fā)組件Java類。組件類負(fù)責(zé)管理代表組件狀態(tài)的屬性。因此,我們必須根據(jù)組件的行為(如輸入組件或輸出組件),給組件選擇適當(dāng)?shù)幕悾▍⒁娗鍐?)。這里描述的組件可進(jìn)行擴(kuò)展javax.faces.component.UIOutput,以顯示指向某個(gè)樣式表文件的URL,或內(nèi)聯(lián)式樣式表的內(nèi)容。該組件可用于在JSF動(dòng)作中將某個(gè)樣式表轉(zhuǎn)換成另一個(gè)樣式表。關(guān)聯(lián)屬性規(guī)定著值的類型:要么是一個(gè)URL,要么是內(nèi)聯(lián)樣式。該組件還必須能夠在向服務(wù)器發(fā)送請(qǐng)求期間,使用經(jīng)過JSF框架處理的對(duì)象,來存儲(chǔ)并修復(fù)自己的狀態(tài)。組件的狀態(tài)由重建對(duì)象所需的重要屬性值組成。JSF框架自動(dòng)調(diào)用saveState()和restoreState()方法,我們可以在組件中實(shí)現(xiàn)這兩種方法來達(dá)到這一目標(biāo)。

清單1. 組件類管理顯示組件狀態(tài)的屬性。可依據(jù)組件的行為,為其選擇一個(gè)適當(dāng)?shù)幕悺T诒纠校摻M件擴(kuò)展javax.faces.component.UIOutput,以顯示指向某個(gè)樣式表文件的URL,或者某個(gè)內(nèi)聯(lián)式樣式表的內(nèi)容。

import javax.faces.component.*;
public class CSSComponent extends UIOutput {
	private Boolean link; 
	public String getFamily() { 
		return "faces.CSSFamily"; 
	} 
	public boolean isLink() { 
		if (link != null) 
			return link.booleanValue(); 
			ValueBinding vb = getValueBinding("link"); 
	if (vb != null) { 
		Boolean bvb = (Boolean) vb.getValue(
			FacesContext.getCurrentInstance()); 
		if (bvb != null) 
			return bvb.booleanValue(); 
	} 
	return false; 
	} 
	public void setLink(boolean link) { 
		this.link = new Boolean(link); 
	} 
	public Object saveState(FacesContext context) { 
		return new Object[] { super.saveState(context), 
			link }; 
	} 
	public void restoreState(FacesContext context, 
	Object stateObj) { 
		Object[] state = (Object[]) stateObj; 
		super.restoreState(context, state[0]); 
		link = (Boolean) state[1]; 
	} 
}

開發(fā)呈現(xiàn)程序。呈現(xiàn)程序有兩個(gè)作用。第一,呈現(xiàn)程序負(fù)責(zé)發(fā)送適當(dāng)?shù)腍TML程序段,該程序段能在客戶端中呈現(xiàn)組件。通常情況下,這個(gè)HTML程序段由一些適于呈現(xiàn)整個(gè)Web瀏覽器的HTML標(biāo)簽組成。此JSF生存周期稱作編碼階段或呈現(xiàn)—響應(yīng)階段。該響應(yīng)階段還能發(fā)送增強(qiáng)客戶端交互性的JavaScript代碼。

  呈現(xiàn)程序的第二個(gè)作用是解析來自客戶端的數(shù)據(jù),從而對(duì)服務(wù)器端的組件狀態(tài)進(jìn)行更新(如用戶在文本字段輸入的文本)。標(biāo)準(zhǔn)呈現(xiàn)程序軟件包具有強(qiáng)制性,但也可以提供其他呈現(xiàn)程序軟件包,用于提供可替換的客戶端表示法或SVG之類的語(yǔ)言(參見參考資料)。通過檢驗(yàn)組件的連接屬性,您實(shí)現(xiàn)的呈現(xiàn)程序(參見清單2)將選擇在HTML頁(yè)面中發(fā)送的CSS樣式。

清單2. 標(biāo)準(zhǔn)呈現(xiàn)程序軟件包具有強(qiáng)制性,但是,您可以使用其他呈現(xiàn)程序軟件包,來提供可替換的客戶端表示法或語(yǔ)言。通過檢驗(yàn)組件的連接屬性,您實(shí)現(xiàn)的呈現(xiàn)程序?qū)⑦x擇在HTML頁(yè)面中發(fā)出的CSS樣式。

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class CSSRenderer extends Renderer {
public void encodeEnd(FacesContext context,
UIComponent component)
	throws IOException {
		super.encodeEnd(context, component);
		if (component instanceof CSSComponent) {
			CSSComponent cssComponent  =
				(CSSComponent) component;
			String css = (String)cssComponent.getValue();
			boolean isLink = cssComponent.isLink();
			if (css != null) 
				if (isLink)  
				context.getResponseWriter().write(
				"<link type='text/css' rel='stylesheet' 
				href='" + css + "'/>");
			else 
				context.getResponseWriter().write(
					"<style>;\n" + css + "\n<style/>\n"); 
		}
	}
}

開發(fā)標(biāo)簽類。同樣,JSF框架提供了用于擴(kuò)展的基類,來編寫與組件相關(guān)的標(biāo)簽。該標(biāo)簽類負(fù)責(zé)定義并呈現(xiàn)將在faces-config.xml文件中應(yīng)用的組件樣式(這種樣式的描述很簡(jiǎn)短)。它還負(fù)責(zé)創(chuàng)建JSF組件(由JSF框架來處理),傳遞JSF標(biāo)簽中所包含的屬性,該屬性用于初始化組件(參見清單3)。

清單3. 該標(biāo)簽類定義了將在faces-config.xml文件中應(yīng)用的組件的樣式和組件呈現(xiàn)方式。

import javax.faces.webapp.UIComponentTag;
public class CSSTag 
	extends UIComponentTag {
	private String value;
	private String link;
	public String getComponentType() {
		return "faces.CSSComponent";
	}
	public String getRendererType() {
		return “HTML.LinkOrInlineRenderer";
	}
	protected void setProperties(UIComponent component) {
		super.setProperties(component);
		Application app = 
			getFacesContext().getApplication();
		if (value != null)
			if (isValueReference(value)) 
				component.setValueBinding("value", 
				app.createValueBinding(value));
			else
				component.getAttributes().put("value", value);
		if (link != null) 
			if (isValueReference(link))
				component.setValueBinding("link",
				app.createValueBinding(link));
			else
				component.getAttributes().put("link",
				new Boolean(link));
	}
	public String getLink() {
		return link;
	}
	public void setLink(String link) {
		this.link = link;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

  該標(biāo)簽提供setter和getter來管理鏈接和值屬性。組件一旦創(chuàng)建,便會(huì)調(diào)用setProperties()方法,對(duì)標(biāo)簽屬性進(jìn)行初始化。每個(gè)標(biāo)簽屬性都無(wú)外乎兩種:要么是文字值,要么是bean屬性的一個(gè)綁定。

編寫標(biāo)簽庫(kù)定義(TLD)。TLD是一個(gè)XML文件,它通過將標(biāo)簽名與相應(yīng)的Java類相關(guān)聯(lián)來描述標(biāo)簽。TLD還描述了標(biāo)簽所允許的屬性(參見清單4)。這個(gè)TLD定義了一個(gè)名為css的標(biāo)簽,該標(biāo)簽綁定到CSSTag類。它還聲明了鏈接和值標(biāo)簽屬性。

清單4. TLD是一個(gè)通過將標(biāo)簽名與相應(yīng)的Java類相關(guān)聯(lián)來描述標(biāo)簽的XML文件。TLD定義了名為css的標(biāo)簽,使其與CSSTag類綁定。它還聲明了鏈接和值標(biāo)簽屬性。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC 
	"-//Sun Microsystems, Inc.//
	DTD JSP Tag Library  1.2//EN" 
	"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	<short-name>custom</short-name>
	<uri>http://www.ilog.com/jviews/tlds/css.tld</uri>
	<description>This tag library contains a tag for a 
		sample custom JSF Component.</description>
	<tag>
		<name>css</name>
		<tag-class>path.to.CSSTag</tag-class>
		<description>A component that displays the style 
			inline or a link a to a css file</description>
		<attribute>
			<name>id</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>The id of this component.
			</description>
		</attribute>
		<attribute>
			<name>binding</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>The value binding expression 
				linking this component to a property in a
				backing bean. If this attribute is set, the 
				tag does not create the component itself but
				retrieves it from the bean property. This
				attribute must be a value binding.
			</description>
		</attribute>
		<attribute>
			<name>value</name>
			<required>true</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>The inline css text or the url to 
				the css file to link.</description>
		</attribute>
		<attribute>
			<name>link</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>Whether the value is a link or 
				the inline style.</description>
		</attribute>
	</tag>
</taglib>

編寫JSF配置文件。為了將某個(gè)JSF組件集成到框架中,您必須提供一個(gè)名為faces-config.xml的配置文件。該文件將組件類型和呈現(xiàn)程序類型(用于JSP定制標(biāo)簽處理程序)與對(duì)應(yīng)的Java類關(guān)聯(lián)起來。它還能描述與每個(gè)組件一同使用的呈現(xiàn)程序(參見清單5)。該文件定義了faces.CSSFamily組件家族。在本例中,該家族由faces.CSSComponent這一個(gè)組件類型(該類型與CSSComponent類綁定)組成。最后,HTML.LinkOrInlineRenderer類型的呈現(xiàn)程序(由CSSComponent類實(shí)現(xiàn))要與faces.CSSFamily家族相關(guān)聯(lián)。

清單5. 該文件將組件類型和呈現(xiàn)程序類型與對(duì)應(yīng)的Java類聯(lián)系起來,并描述與每個(gè)組件一同使用的呈現(xiàn)程序。它還定義了faces.CSSFamily組件家族。
<!DOCTYPE faces-config PUBLIC 
	"-//Sun Microsystems, Inc.//
	DTD JavaServer Faces Config 1.0//EN" 
	"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
	<component>
		<component-type>faces.CSSComponent
		</component-type>
		<component-class>path.to.CSSComponent
		</component-class>
		<component-extension>
			<component-family>faces.CSSFamily
			</component-family>
			<renderer-type>HTML.LinkOrInlineRenderer
			</renderer-type>
		</component-extension>
	</component>
	<render-kit>
		<renderer>
			<component-family>faces.CSSFamily
			</component-family>
			<renderer-type> HTML.LinkOrInlineRenderer 
			</renderer-type>
			<renderer-class>path.to.CSSRenderer
			</renderer-class>
		</renderer>
	/render-kit>
</faces-config>

開始制圖

  如果您希望將自己的組件集成到JSF-enabled IDE中,您還可以提供補(bǔ)充說明。比如說,除提供其他的設(shè)計(jì)時(shí)信息外,還可以提供一個(gè)名為sun-faces-config.xml的XML配置文件,用于描述應(yīng)在IDE中公開的組件屬性。

  既然已經(jīng)看到如何創(chuàng)建一個(gè)簡(jiǎn)單的JSF組件,不妨再來看看怎樣創(chuàng)建一個(gè)圖形JSF組件。我們將遵循同樣的基本步驟來設(shè)計(jì)一個(gè)高級(jí)JSF圖形組件。讓我們以一個(gè)圖形組件(如ILOG JSF圖形組件)為例,通過一組分類,該組件為數(shù)據(jù)值分布提供了可視化表示。該圖形能夠以條型統(tǒng)計(jì)圖、圓形分格統(tǒng)計(jì)圖和氣泡式統(tǒng)計(jì)圖等各種顯示方法來顯示數(shù)據(jù)集合。該JSF圖形組件有兩個(gè)初始設(shè)計(jì)限制:

  我們已經(jīng)擁有Java圖形bean組件,它具備所有圖形顯示能力。該組件可以顯示很多圖形,而且可定制性很高。在理想情況下,我們希望利用bean組件,使用它的功能來構(gòu)成我們的JSF組件的基礎(chǔ)。

  普通JSF應(yīng)用程序需要重新載入整個(gè)頁(yè)面以更新視圖。這種方法適合基于表單的應(yīng)用程序,但在很多情況下卻不適用于高度圖形化的用戶界面。因此,我們的JSF圖形組件必須能在不更新整個(gè)頁(yè)面的前提下處理某些簡(jiǎn)單的導(dǎo)航,以提供更好的用戶體驗(yàn)。

  以下是滿足這些需求的解決方案:該JSF圖形組件將管理圖形bean組件,包括創(chuàng)建圖形bean、定制該bean以及使該bean可用于服務(wù)器端操作。呈現(xiàn)JSF組件將分為兩個(gè)階段完成。JSF呈現(xiàn)程序會(huì)產(chǎn)生一個(gè)<img>標(biāo)簽和一套JavaScript對(duì)象(參見圖2)。客戶端將請(qǐng)求服務(wù)器發(fā)回一張圖像。這一請(qǐng)求由某個(gè)servlet完成,該servlet獲得圖形bean,并利用圖形提供的方法生成一幅圖像(參見圖3)。任何只改變?cè)搱D形外觀的進(jìn)一步用戶交互(放大、掃視、更改樣式表等)都會(huì)引起圖形的一次增量更新。如果客戶端不只是要求對(duì)圖形圖像進(jìn)行更新,那么將提交該頁(yè)面(參見圖4)。

  圖2JSF圖形組件管理圖形bean組件,包括創(chuàng)建圖形bean、對(duì)其進(jìn)行定制,并使其可用于服務(wù)器端動(dòng)作。JSF呈現(xiàn)程序生成一個(gè)<img>標(biāo)簽和一套JavaScript對(duì)象。

  圖3 客戶機(jī)通過servlet要求服務(wù)器獲得一張圖像。該servlet獲得圖形bean,并通過由圖形提供的方法生成一幅圖像。

  圖4如果客戶端不只是要求對(duì)圖形外觀的進(jìn)行更新,那么頁(yè)面將被提交。

  JSF圖形組件還配有一套附加的JSF組件。overview可顯示該圖形整體視圖,顯示一個(gè)代表圖形視圖的長(zhǎng)方形,還應(yīng)允許用戶掃描可視區(qū)域。legend組件可顯示數(shù)據(jù)集合的相關(guān)信息,還能自行在圖形中顯示,依被顯示數(shù)據(jù)的樣式而定。也能提供客戶端的interactors如掃描和放大,這些功能可看成是客戶端交互,表示與圖形的交互不會(huì)像一次正常的JSF交互那樣重新載入整個(gè)頁(yè)面。

  要想呈現(xiàn)圖形組件,只需使用chartView標(biāo)簽:

<jvcf:chartView id="c" style="width:500px;height:300px" … />

  該數(shù)據(jù)在HTML頁(yè)面中作為圖像顯示。該圖像由servlet創(chuàng)建,旨在響應(yīng)一次HTTP請(qǐng)求(該請(qǐng)求包括指定結(jié)果圖像、生成圖像映射以及生成內(nèi)聯(lián)式圖例等各種參數(shù))。結(jié)果圖像隨之被嵌入客戶端DOM,頁(yè)面中只有圖像自身這一部分被更新。

應(yīng)用程序核心部件

  讓我們看看簡(jiǎn)單的定制JSF組件和高級(jí)圖形組件之間的一些區(qū)別。JSF圖形組件類很像一個(gè)標(biāo)準(zhǔn)組件,不過是多了一個(gè)可訪問圖形bean(該圖形bean負(fù)責(zé)生成在HTML頁(yè)面中顯示的圖像)的圖形屬性。JSF組件可以通過某個(gè)綁定值或在當(dāng)前會(huì)話中對(duì)這個(gè)圖形bean進(jìn)行局部檢索。當(dāng)JSF圖形組件成為某個(gè)應(yīng)用程序的核心部件時(shí),可選的JSF組件(如概覽或圖例)便與主圖形相關(guān)聯(lián),來顯示附加信息(見清單6)。

清單6. 當(dāng)JSF圖形組件成為某個(gè)應(yīng)用程序的核心部件時(shí),可選的JSF組件便與主圖形相關(guān)聯(lián),來顯示附加信息。

<jvcf:chartZoomInteractor id="chartZoomInteractor" 
	XZoomAllowed="true" 
	YZoomAllowed="true" /> 
<jvcf:chartView id="chartView" 
						 chart="#{myBean.chart}" 
					 servlet="demo.ImageMapServlet" 
			interactorId="chartZoomInteractor" 
						 width="500" 
						height="300" 
			 styleSheets="/data/line.css" 
			waitingImage="data/images/wait.gif" 
			 imageFormat="PNG"  />
<jvcf:chartOverview id="chartOverview" 
					style="height:100;width:150px" 
						viewId="chartView" 
						lineWidth="3" 
						lineColor="red" />

<jvcf:chartLegend id="legendView" 
							viewId="chartView" 
							 width="400" 
							height="180" 
							layout="vertical" 
				waitingImage="data/images/wait.gif" />

  呈現(xiàn)程序是實(shí)現(xiàn)這個(gè)JSF的一大難點(diǎn)。如前所述,呈現(xiàn)程序并不生成簡(jiǎn)單的HTML,而是生成由HTML(<IMG> tag)和JavaScript proxy(代理程序)組成的動(dòng)態(tài)HTML(DHTML)。

  Proxy是一個(gè)負(fù)責(zé)管理客戶機(jī)組件圖像顯示的JavaScript類實(shí)例。該對(duì)象是服務(wù)器端Java組件類在客戶端顯示;它與組件類具有相同屬性。頁(yè)面上的每個(gè)組件、圖形及其配件都有一個(gè)proxy實(shí)例。呈現(xiàn)JavaScript時(shí),在每個(gè)可視的JavaScript變量上使用facesContext.getExternalContext().encodeNamespace(name)方法是個(gè)很好的實(shí)踐。這樣做在今后方便地將組件集成到到JSR 168-compliant端口環(huán)境中。

  為舉例說明客戶機(jī)上的proxy,必須在頁(yè)面上導(dǎo)入JavaScript支持庫(kù)。為保持客戶端盡量瘦,需要基于JavaScript庫(kù)支持的proxy類,對(duì)JavaScript庫(kù)進(jìn)行模塊化。因此,需要給每個(gè)proxy類輸入一套不同的、有可能重疊的庫(kù)。圖形呈現(xiàn)的困難部分,出現(xiàn)在發(fā)送這些script庫(kù)的階段。每個(gè)組件的呈現(xiàn)程序都要聲明自己需要哪個(gè)庫(kù),以及什么時(shí)候發(fā)送引用的庫(kù),必須認(rèn)清已發(fā)送的庫(kù),以避免重復(fù)。僅在頁(yè)面呈現(xiàn)期間的存在script管理器負(fù)責(zé)這項(xiàng)篩選工作。每當(dāng)呈現(xiàn)程序想要發(fā)送整套庫(kù)輸入時(shí),它都會(huì)向篩選出已發(fā)送庫(kù)的script管理器提供列表。

  客戶端proxy的目的在于允許編寫腳本,并避免不必要的頁(yè)面更新。一旦呈現(xiàn)了圖形,在客戶端便可使用proxy,以便動(dòng)態(tài)安裝interactor,并顯示或隱藏圖像映射。Proxy對(duì)象也可供支持JavaScript鼠標(biāo)事件處理的常規(guī)JSF組件使用。

<jvcf:chartView id=
	"chartView" .. />
<h:selectBooleanCheckbox id=
	"genImageMap" onclick=
	"chartView.setGenerateImageMap(
	this.checked ? true : false, 
 true);" />

  對(duì)組件客戶端proxy進(jìn)行局部修改的問題在于,其狀態(tài)不再與服務(wù)器上的Java組件的狀態(tài)同步。為解決這個(gè)問題,proxy使用一個(gè)隱藏的輸入標(biāo)簽(<INPUT TYPE="HIDDEN">)來保存客戶機(jī)上的新狀態(tài)。當(dāng)執(zhí)行某個(gè)標(biāo)準(zhǔn)JSF動(dòng)作并提交頁(yè)面時(shí),呈現(xiàn)程序?qū)⒔馕鲈撾[藏狀態(tài),使客戶機(jī)與服務(wù)器同步。這種行為需要呈現(xiàn)程序類中有專門的破解行為。標(biāo)準(zhǔn)破解方法得以改進(jìn),以便解析來自客戶機(jī)的狀態(tài),并更新服務(wù)器端組件的狀態(tài)。

測(cè)試實(shí)例

  圖形及其相關(guān)組件之間的關(guān)聯(lián)由標(biāo)記引用與綁定來完成。為使頁(yè)面設(shè)計(jì)具有靈活性,一個(gè)組件可以在呈現(xiàn)之前被引用。因此,在呈現(xiàn)時(shí)間內(nèi),如果某個(gè)組件屬性引用另一個(gè)尚未呈現(xiàn)的組件,那么,將延遲發(fā)送依賴于客戶機(jī)進(jìn)行解決的JavaScript代碼,直到呈現(xiàn)已引用的組件。此工作可由依賴性管理器完成。

為證實(shí)這一點(diǎn),不妨看一個(gè)典型實(shí)例,該實(shí)例涉及某個(gè)概覽,該概覽引用一張圖形。

<jvcf:overview viewId=
	"chart" [...] /> 
<jvcf:chartView id=
	"chart" [....] />

  存在兩種情況。被引用圖形組件已經(jīng)呈現(xiàn),因此不存在任何問題

JSP:
<jvcf:chartView id=
	"chart" [....] />
<jvcf:overview viewId=
	"chart" id="overview" [...] /> 

render:
[...]
var chart = 
	new IlvChartViewProxy ( .. );
[...]

var overview= 
	new IlvFacesOverviewProxy (
	 .. );
overview.setView(chart);
[...]

  已引用圖形的組件在依賴的概覽組件之前不會(huì)呈現(xiàn)。既然如此,可在依賴性管理器上注冊(cè)一個(gè)組件創(chuàng)建監(jiān)視器。已引用圖形組件最終呈現(xiàn)時(shí),其呈現(xiàn)程序會(huì)通知自己創(chuàng)建的依賴性管理器。此時(shí),將發(fā)送解決依賴性所需的代碼:

JSP:
<jvf:overview viewId=
	"chart" id="overview" [...] /> 
<jvdf:chartView id=
	"chart" [....] />

render:
[...]
var overview = 
	new IlvFacesOverviewProxy (
	 .. );
[...]

var chart = 
	new IlvChartViewProxy ( .. );
overview.setView(chart);
[...]

  開發(fā)JSF組件的目的之一,是能夠?qū)⑺鼈儜?yīng)用于任何與JSF兼容的IDE。盡管如此,JSF兼容性并不足以保證這種設(shè)計(jì)時(shí)集成將會(huì)有效。下面是在開發(fā)JSF組件過程中,為了便于在今后與IDE集成需要注意的一些簡(jiǎn)單思想:

  首先,定制JSF組件應(yīng)該提供一個(gè)基本HTML呈現(xiàn)程序。在設(shè)計(jì)時(shí),JSF IDE不能呈現(xiàn)請(qǐng)求有效數(shù)據(jù)或app服務(wù)器連接的動(dòng)態(tài)圖形組件。因此,帶有復(fù)雜的或非傳統(tǒng)的(比如不是HTML)呈現(xiàn)程序的組件,應(yīng)該使用Beans.isDesignTime()來確定是提供一個(gè)基本HTML表示法,還是提供真正的組件呈現(xiàn)程序。

  另一個(gè)設(shè)計(jì)時(shí)問題是組件的位置和大小。不同IDE使用不同的標(biāo)志和屬性。能夠調(diào)整大小的組件(如一幅圖像)應(yīng)能處理定義大小的不同方式。

  最后,為了與IDE集成,組件必須提供尚未被JSF說明定義的補(bǔ)充信息。遺憾的是,當(dāng)前每個(gè)IDE都需要特殊處理程序來集成組件,即:在一種情況就需要XML文件,而在另一種情況下需要eclipse插件,如此等等。下一個(gè)JSF JSR(2.0版)的主要目的之一,將是指定附加的元數(shù)據(jù)格式。

  如您所見,編寫一個(gè)簡(jiǎn)單的JSF組件并不難,因?yàn)榭蚣芤呀?jīng)完成了大部分工作。JSF框架管理著組件狀態(tài)、呈現(xiàn)程序等等。在本文中,我們已經(jīng)擴(kuò)展了這些基本概念,來設(shè)計(jì)一個(gè)能夠顯示復(fù)雜元數(shù)據(jù)、提供增量更新、支持大量客戶端交互并與配套組件協(xié)作的高級(jí)圖形JSF組件。支持這些特點(diǎn)需要對(duì)基本JSF組件的結(jié)構(gòu)進(jìn)行許多改進(jìn)。當(dāng)然,增量更新的概念今后對(duì)JSF框架將是一個(gè)很好的完善,因?yàn)樗辉试S呈現(xiàn)頁(yè)面已改變的部分,避免了更新整個(gè)頁(yè)面。按照J(rèn)SF說明書工作往往不足以確保組件完全集成到JSF IDE中;一個(gè)新JSR應(yīng)能及時(shí)解決這些難題。盡管存在缺陷,JSF框架仍能極大地加快Web組件開發(fā)速度、方便的融合來自各種資源的組件,以創(chuàng)建完整的、復(fù)雜的Web應(yīng)用程序。

參考資料

作者簡(jiǎn)介

  Marc Durocher是ILOG的一名軟件架構(gòu)師,ILOG是企業(yè)級(jí)軟件組件和服務(wù)的主要提供商。Marc Durocher在ILOG負(fù)責(zé)開發(fā)ILOG JViews生產(chǎn)線上的JSF組件。可以通過mdurocher@ilog.fr聯(lián)系Marc。

原文出處

http://www.ftponline.com/weblogicpro/2005_03/magazine/features/mdurocher/