Servlet和JSP技術(shù)是用Java開(kāi)發(fā)服務(wù)器端應(yīng)用的主要技術(shù),是開(kāi)發(fā)商務(wù)應(yīng)用表示端的標(biāo)準(zhǔn)。Java開(kāi)發(fā)者喜歡使用它有多種原因,其一是對(duì)于已經(jīng)熟悉Java語(yǔ)言的開(kāi)發(fā)者來(lái)說(shuō)這個(gè)技術(shù)容易學(xué)習(xí);其二是Java把“一次編寫,到處運(yùn)行”的理念帶入到Web應(yīng)用中,實(shí)現(xiàn)了“一次編寫,到處實(shí)現(xiàn)”。而且更為重要的是,如果遵循一些良好的設(shè)計(jì)原則的話,就可以把表示和內(nèi)容相分離,創(chuàng)造出高質(zhì)量的、可以復(fù)用的、易于維護(hù)和修改的應(yīng)用程序。比方說(shuō),在HTML文檔中如果嵌入過(guò)多的Java代碼(scriptlet),就會(huì)導(dǎo)致開(kāi)發(fā)出來(lái)的應(yīng)用非常復(fù)雜、難以閱讀、不容易復(fù)用,而且對(duì)以后的維護(hù)和修改也會(huì)造成困難。事實(shí)上,在CSDN的JSP/Servlet論壇中,經(jīng)常可以看到一些提問(wèn),代碼很長(zhǎng),可以邏輯卻不是很清晰,大量的HTML和Java代碼混雜在一起,讓人看得一頭霧水。這就是隨意開(kāi)發(fā)的弊端。
如果你已經(jīng)基本了解JSP和Servlet的各項(xiàng)技術(shù)(最好也開(kāi)發(fā)過(guò)一些Web應(yīng)用),那么我們可以一起探討一下如何開(kāi)發(fā)“好”的應(yīng)用的一些指導(dǎo)原則。我們首先對(duì)Servlet和JSP技術(shù)做一個(gè)瀏覽。
Servlet和JSP概覽
早期的動(dòng)態(tài)網(wǎng)頁(yè)主要采用CGI(Common Gateway Interface,公共網(wǎng)關(guān)接口)技術(shù),你可以使用不同的語(yǔ)言編寫CGI程序,如VB、C/C++或Delphi等。雖然CGI技術(shù)發(fā)展成熟且功能強(qiáng)大,但由于編程困難、效率低下、修改復(fù)雜等缺點(diǎn),所以有逐漸被取代的趨勢(shì)。在所有的新技術(shù)中,JSP/Servlet具備更高效、更容易編程、功能更強(qiáng)、更安全和具有良好的可移植性,因而被許多人認(rèn)為是未來(lái)最有發(fā)展前途的動(dòng)態(tài)網(wǎng)站技術(shù)。
與CGI相似,Servlet支持請(qǐng)求/響應(yīng)模型。當(dāng)一個(gè)客戶向服務(wù)器遞交一個(gè)請(qǐng)求時(shí),服務(wù)器把請(qǐng)求送給Servlet,Servlet負(fù)責(zé)處理請(qǐng)求并生成響應(yīng),然后送給服務(wù)器,再由服務(wù)器發(fā)送給客戶。與CGI不同的是,Servlet沒(méi)有生成新的進(jìn)程,而是與HTTP Server處于同一進(jìn)程中。它通過(guò)使用線程技術(shù),減小了服務(wù)器的開(kāi)銷。Servlet處理請(qǐng)求的過(guò)程是這樣的:當(dāng)收到來(lái)自客戶端的請(qǐng)求后,調(diào)用service方法,該方法中Servlet先判斷到來(lái)的請(qǐng)求是什么類型的(GET/POST/HEAD…),然后調(diào)用相應(yīng)的處理方法(doGet/doPost/doHead…)并生成響應(yīng)。
別看這么復(fù)雜,其實(shí)簡(jiǎn)單說(shuō)來(lái)Servlet就是一個(gè)Java類。與一般類的不同之處是,這個(gè)類運(yùn)行在一個(gè)Servlet容器內(nèi),可以提供session管理和對(duì)象生命周期管理。因而當(dāng)你使用Servlet的時(shí)候,你可以得到Java平臺(tái)的所有好處,包括安全性管理、使用JDBC訪問(wèn)數(shù)據(jù)庫(kù)以及跨平臺(tái)的能力。而且,Servlet使用線程,因而可以開(kāi)發(fā)出效率更高的Web應(yīng)用。
JavaServer Pages (JSP)
JSP技術(shù)是J2EE的一個(gè)關(guān)鍵技術(shù),它在更高一級(jí)的層次上抽象Servlet。它可以讓常規(guī)靜態(tài)HTML與動(dòng)態(tài)產(chǎn)生的內(nèi)容相結(jié)合,看起來(lái)像一個(gè)HTML網(wǎng)頁(yè),卻作為Servlet來(lái)運(yùn)行。現(xiàn)在有許多商業(yè)應(yīng)用服務(wù)器支持JSP技術(shù),比如BEA WebLogic、IBM WebSphere、 JRun等等。使用JSP比用Servlet更簡(jiǎn)單。如果你有一個(gè)支持JSP的Web服務(wù)器,并且有一個(gè)JSP文件,你可以把它放倒任何靜態(tài)HTML文件可以放置的位置,不用編譯,不用打包,也不用進(jìn)行ClassPath的設(shè)置,就可以像訪問(wèn)普通網(wǎng)頁(yè)那樣訪問(wèn)它,服務(wù)器會(huì)自動(dòng)幫你做好其他的工作。
JSP工作原理
JSP 文件看起來(lái)就像一個(gè)普通靜態(tài)HTML文件,只不過(guò)里面包含了一些Java代碼。它使用.jsp的后綴,用來(lái)告訴服務(wù)器這個(gè)文件需要特殊的處理。當(dāng)我們?cè)L問(wèn)一個(gè)JSP頁(yè)面的時(shí)候,這個(gè)文件首先會(huì)被JSP引擎翻譯為一個(gè)Java源文件,其實(shí)就是一個(gè)Servlet,并進(jìn)行編譯,然后像其他Servlet一樣,由Servlet引擎來(lái)處理。Servlet引擎裝載這個(gè)類,處理來(lái)自客戶的請(qǐng)求,并把結(jié)果返回給客戶,如下圖所示:

圖1: 調(diào)用JSP頁(yè)面的流程
以后再有客戶訪問(wèn)這個(gè)頁(yè)面的時(shí)候,只要該文件沒(méi)有發(fā)生過(guò)更改,JSP引擎就直接調(diào)用已經(jīng)裝載的Servlet。如果已經(jīng)做過(guò)修改的話,那就會(huì)再次執(zhí)行以上過(guò)程,翻譯、編譯并裝載。其實(shí)這就是所謂的“第一人懲罰”。因?yàn)槭状卧L問(wèn)的時(shí)候要執(zhí)行一系列以上的過(guò)程,所以會(huì)耗費(fèi)一些時(shí)間;以后的訪問(wèn)就不會(huì)這樣了。
開(kāi)發(fā)原則
這一部分我們列出一些開(kāi)發(fā)原則,重點(diǎn)是JSP頁(yè)面。關(guān)于如何分離表現(xiàn)和內(nèi)容的MVC因?yàn)橐婕暗?a class=channel_keylink >JSP和Servlet的整合,我們稍候再談。
不要在JSP頁(yè)面中嵌入過(guò)量的Java代碼:對(duì)于非常簡(jiǎn)單或是測(cè)試性的代碼,把所有的Java 代碼直接放入JSP頁(yè)面內(nèi)是沒(méi)有問(wèn)題的。但是這種方法不應(yīng)該被過(guò)度使用,否則就會(huì)產(chǎn)生一大堆HTML和Java混合起來(lái)的代碼,讓人難以閱讀和理解。解決方法是寫一個(gè)單獨(dú)的類,用來(lái)執(zhí)行相關(guān)的計(jì)算。一旦這個(gè)類測(cè)試通過(guò),就可以把它放在任何執(zhí)行同樣計(jì)算的場(chǎng)合中。這樣可以促進(jìn)代碼的復(fù)用。
選擇合適的包含(include)機(jī)制: 如果一個(gè)應(yīng)用中每個(gè)頁(yè)面有一樣的抬頭和底部,或者還有導(dǎo)航條,那么就應(yīng)該把它們放到一個(gè)單獨(dú)的文件中,然后在每一個(gè)頁(yè)面中使用包含機(jī)制把它們加入到這個(gè)頁(yè)面中:
Include 指令: <%@ include file="filename" %>或等效xml語(yǔ)法
<jsp:directive.includefile=”filename” />
Include 動(dòng)作: <jsp:include page="page.jsp" flush="true" />
Include指令是當(dāng)JSP頁(yè)面翻譯為Servlet的時(shí)候包含另外一個(gè)文件,Include 動(dòng)作是當(dāng)請(qǐng)求時(shí)包含另外一個(gè)文件的輸出。如果被包含的文件不是經(jīng)常改動(dòng)的時(shí)候,我建議使用Include 指令,這樣速度更快。如果被包含的文件需要不時(shí)改動(dòng)或者知道請(qǐng)求時(shí)才能決定需要包含的內(nèi)容時(shí),那么應(yīng)該使用Include 動(dòng)作。
如果你使用JSP標(biāo)準(zhǔn)標(biāo)記庫(kù)(JavaServer pages Standard Tag Library即JSTL)的話,那么還有第三中包含機(jī)制<c:import>,可以用來(lái)包含本地或者遠(yuǎn)程的資源。例如:
<c:import url="./copyright.html"/>
<c:import url="http://www.somewhere.com/hello.xml"/>
不要把業(yè)務(wù)邏輯和表示混合在一起: 復(fù)雜的應(yīng)用涉及大量的代碼,因而把業(yè)務(wù)邏輯和前端的表示相分離就顯得格外重要,這種分離可以讓任何一方的變化不會(huì)影響到另外一方。所以,所有的JSP代碼都應(yīng)該限制在表示層,可是如果這樣的話,你如何實(shí)現(xiàn)你的業(yè)務(wù)邏輯呢?這就是JavaBean所做的事情。JavaBean技術(shù)是一個(gè)獨(dú)立于平臺(tái)的組件模型,它讓開(kāi)發(fā)者編寫、測(cè)試通過(guò)一個(gè)組件后,可以隨處使用,提高了復(fù)用性。在JSP技術(shù)中,JavaBean實(shí)現(xiàn)了業(yè)務(wù)邏輯部分,它把數(shù)據(jù)返回給JSP頁(yè)面,由JSP頁(yè)面負(fù)責(zé)格式化數(shù)據(jù)并輸出到客戶端的瀏覽器。在JSP頁(yè)面中使用JavaBean組件的好處是:
產(chǎn)生了可以復(fù)用的組件:任何應(yīng)用都可以使用這些組件
可以把業(yè)務(wù)邏輯和表示相分離:你可以修改數(shù)據(jù)的顯示方式而不用考慮業(yè)務(wù)邏輯。這樣做的結(jié)果也可以明確工作中開(kāi)發(fā)人員的分工,網(wǎng)頁(yè)開(kāi)發(fā)人員可以把精力放到如何顯示數(shù)據(jù)上,Java開(kāi)發(fā)者則更為關(guān)注業(yè)務(wù)邏輯的實(shí)現(xiàn)。
對(duì)于JavaBean你不用提供源代碼,這樣你的代碼就不會(huì)被瀏覽器網(wǎng)頁(yè)的人輕易獲得,可以保護(hù)你的勞動(dòng)成果。
如果你的應(yīng)用中使用了EJB組件,那么業(yè)務(wù)邏輯就應(yīng)該放置在EJB中,因?yàn)镋JB模型提供了生命周期管理、事務(wù)管理以及多客戶訪問(wèn)域?qū)ο螅‥ntity Beans)。你可以仔細(xì)看一下Enterprise BluePrints中的例子,它就是這么做的。
使用定制的標(biāo)記: 上面我們已經(jīng)討論過(guò),把所有Java代碼嵌入到JSP頁(yè)面內(nèi)并不合適,因?yàn)榫W(wǎng)頁(yè)開(kāi)發(fā)人員并不一定懂得Java語(yǔ)言,更難以理解Java語(yǔ)法。JavaBean可以封裝很多Java代碼,不過(guò)在JSP頁(yè)面內(nèi)使用JavaBean仍然要求頁(yè)面開(kāi)發(fā)人員了解一些Java語(yǔ)法。
JSP技術(shù)中包含了定制標(biāo)記庫(kù)的功能。Java開(kāi)發(fā)人員可以生成自己的標(biāo)記庫(kù),這樣網(wǎng)頁(yè)設(shè)計(jì)人員就可以使用類似HTML的語(yǔ)法來(lái)使用這些標(biāo)記。編寫和使用自己定制的標(biāo)記庫(kù)可以在更大程度上促進(jìn)業(yè)務(wù)邏輯和表示的分離。使用定制標(biāo)記庫(kù)主要有以下好處:
可以消除在JSP頁(yè)面中使用scriptlet 標(biāo)記使用的任何參數(shù)都可以通過(guò)屬性傳入,從而不需要使用Java代碼就可以達(dá)到希望的目的。
可以簡(jiǎn)化使用 網(wǎng)頁(yè)設(shè)計(jì)人員不需要學(xué)會(huì)使用Java語(yǔ)法,他們可以用類似HTML語(yǔ)法就可以使用標(biāo)記。
不懂Java的網(wǎng)頁(yè)設(shè)計(jì)人員可以使用標(biāo)記庫(kù)來(lái)完成單獨(dú)使用HTML不能完成的任務(wù)。
提高了復(fù)用性 標(biāo)記庫(kù)完全可以復(fù)用,這可以節(jié)省開(kāi)發(fā)和測(cè)試的時(shí)間。Scriptlet代碼只能在“拷貝粘貼”級(jí)別上進(jìn)行“復(fù)用”。
簡(jiǎn)單說(shuō)來(lái),你可以像用HTML構(gòu)建表示層一樣使用標(biāo)記庫(kù)完成非常復(fù)雜的任務(wù)。下面是表頁(yè)標(biāo)記庫(kù)的一些注意事項(xiàng):
1. 保持簡(jiǎn)潔性:如果一個(gè)標(biāo)記需要好幾個(gè)屬性的話,那么盡可能把它分為幾個(gè)標(biāo)記。
2. 保持復(fù)用性:同標(biāo)記的使用人員(網(wǎng)頁(yè)設(shè)計(jì)人員)多多交流,盡量開(kāi)發(fā)出可以高度復(fù)用的標(biāo)記庫(kù)。
3. 不要一切都從頭開(kāi)始:現(xiàn)在已經(jīng)有一些可以免費(fèi)使用的標(biāo)記庫(kù),比如Jakarta Taglibs。如果你要用到一個(gè)標(biāo)記,先看看是不是已經(jīng)有現(xiàn)成的可以使用。
不要“重新發(fā)明輪子”,不要一切從頭開(kāi)始: 通過(guò)定制組件可以提高復(fù)用性,不過(guò)定制組件仍然需要編寫、測(cè)試和調(diào)試程序。問(wèn)題是這個(gè)事情別人可能已經(jīng)實(shí)現(xiàn)了,而且你的實(shí)現(xiàn)方式并不一定比人家做得更好。這就是JSP標(biāo)準(zhǔn)標(biāo)記庫(kù)(JavaServer Pages Standard Tag Library, JSTL)要做的事情(JSTL請(qǐng)參考JSTL官方網(wǎng)站)。JSTL提供了循環(huán)、讀屬性、遍歷各種數(shù)據(jù)結(jié)構(gòu)、條件表達(dá)式求值等各種標(biāo)記。它也提供了一些復(fù)雜的標(biāo)記,甚至像解析XML文檔的標(biāo)記它都有。所以如果你要用到一個(gè)標(biāo)記的話,最好先看看有沒(méi)有別人已經(jīng)實(shí)現(xiàn)的可以使用,而不要次次從頭開(kāi)始,自己搞一套。
使用JSTL表達(dá)使語(yǔ)言(JSTL Expression Language): 傳遞給JSP頁(yè)面的數(shù)據(jù)一般通過(guò)JSP作用域屬性或者請(qǐng)求參數(shù)來(lái)進(jìn)行。專門為網(wǎng)頁(yè)開(kāi)發(fā)者設(shè)計(jì)的表達(dá)式語(yǔ)言(Expression Language, EL)把使用作用域屬性傳遞信息作為從業(yè)務(wù)邏輯向JSP頁(yè)面?zhèn)鬟f信息的標(biāo)準(zhǔn)方式。這里要注意的是,EL只是JSP技術(shù)中關(guān)鍵的一個(gè)方面,并不是一種通用的程序設(shè)計(jì)語(yǔ)言。相反,它只是一種數(shù)據(jù)訪問(wèn)語(yǔ)言,它可以簡(jiǎn)化應(yīng)用程序的數(shù)據(jù)的訪問(wèn),不用Scriptlet和請(qǐng)求時(shí)表達(dá)式求值就可以訪問(wèn)數(shù)據(jù)。
在JSP中,網(wǎng)頁(yè)設(shè)計(jì)師要使用表達(dá)式語(yǔ)法<%= name %>或JavaBean組件來(lái)取得某些變量或屬性的值,例如:
<tagLib:tag attribute="<%=
pageContext.getAttribute("name") %>">
或
<%= aCustomerBean.getAddress().getCountry() %>
表達(dá)使語(yǔ)言讓網(wǎng)頁(yè)設(shè)計(jì)師可以使用簡(jiǎn)化的語(yǔ)法來(lái)訪問(wèn)信息。如果你只是要訪問(wèn)一個(gè)簡(jiǎn)單的變量,你可以使用這樣的語(yǔ)法:
<tagLib:tag attribute="${name}">
如果你要訪問(wèn)一個(gè)嵌套JavaBean的屬性,你可以這樣:
<tagLib:tag attribute ="${
aCustomerBean.address.country}">
表達(dá)式語(yǔ)言(EL)借用了JavaScript 的語(yǔ)法,所以如果你對(duì)JavaScript 很熟悉的話,你就會(huì)覺(jué)得巨爽。
使用過(guò)濾器(filter): 過(guò)濾器是一個(gè)對(duì)象,可以傳輸請(qǐng)求或修改響應(yīng)。它可以在請(qǐng)求到達(dá)Servlet/JSP之前對(duì)其進(jìn)行預(yù)處理,而且能夠在響應(yīng)離開(kāi)Servlet/JSP之后對(duì)其進(jìn)行后處理。所以如果你有幾個(gè)Servlet/JSP需要執(zhí)行同樣的數(shù)據(jù)轉(zhuǎn)換或頁(yè)面處理的話,你就可以寫一個(gè)過(guò)濾器類,然后在部署描述文件(web.xml)中把該過(guò)濾器與對(duì)應(yīng)的Servlet/JSP聯(lián)系起來(lái)。
創(chuàng)建過(guò)濾器其實(shí)很容易,你只須實(shí)現(xiàn)javax.servlet.Filter接口及它的三個(gè)方法:
public void init(FilterConfig config)
public void doFilter(ServletRequest req, ServletResponse rep,
FilterChain chain)
public void destroy()
這樣,你就可以完成你的過(guò)濾器。
使用可移植的安全模型:大部分的應(yīng)用服務(wù)器都提供了安全模型,不過(guò)一般它們都是針對(duì)某一個(gè)服務(wù)器或某一個(gè)廠商專有的。如果你的應(yīng)用需要移植的話,那么你的應(yīng)用最好使用可以移植的安全模型。如果你的應(yīng)用有一些預(yù)先定義的固定用戶的話,那么你可以使用FROM驗(yàn)證和BASIC驗(yàn)證。可是如果你要?jiǎng)討B(tài)生成客戶的話(一般都是這種情況),你可能就需要使用服務(wù)器特定的API來(lái)創(chuàng)建和管理用戶。這樣當(dāng)你的應(yīng)用移植到另外一個(gè)服務(wù)器時(shí),你可能就會(huì)碰到API不兼容的問(wèn)題。這種情況下,最好的解決方法是使用適配器(Adapter)模式(如果你對(duì)設(shè)計(jì)模式不熟悉的話,請(qǐng)參看GoF的《設(shè)計(jì)模式》一書(shū))。
用數(shù)據(jù)庫(kù)來(lái)保存持久性數(shù)據(jù): Servlet/JSP中可以使用HttpSession對(duì)象也就是會(huì)話對(duì)象來(lái)保存用戶的臨時(shí)數(shù)據(jù)。不過(guò)如果你想保存持久性數(shù)據(jù)的時(shí)候,你應(yīng)該使用數(shù)據(jù)庫(kù),數(shù)據(jù)保存數(shù)據(jù)會(huì)更安全,而且對(duì)客戶所用的瀏覽器沒(méi)有什么要求。這樣即使你的應(yīng)用服務(wù)器由于某種原因崩潰了,你的數(shù)據(jù)依然良好。
高速緩存頁(yè)面: 應(yīng)用程序中總有一些東西是相對(duì)固定的,而另外一些東西是經(jīng)常變化的。你應(yīng)該使用靜態(tài)的HTML文檔來(lái)存儲(chǔ)那些相對(duì)固定的內(nèi)容,這樣客戶端就可以進(jìn)行高速緩存,客戶每次訪問(wèn)你的應(yīng)用時(shí),只需訪問(wèn)已經(jīng)改動(dòng)的部分。這樣可以加快客戶的訪問(wèn)速度。
使用連接池: 如果你要自己寫數(shù)據(jù)庫(kù)訪問(wèn)代碼的話,我覺(jué)得使用你應(yīng)該學(xué)會(huì)如何使用數(shù)據(jù)庫(kù)連接池技術(shù)。每一個(gè)服務(wù)器都有針對(duì)數(shù)據(jù)庫(kù)連接池的配置文檔,你要學(xué)習(xí)一下如何使用。數(shù)據(jù)庫(kù)連接池可以加速你的應(yīng)用的數(shù)據(jù)訪問(wèn)的速度,而且由于服務(wù)器替你管理了數(shù)據(jù)庫(kù)連接,這可以節(jié)省你的很多工作。
緩存數(shù)據(jù)庫(kù)的訪問(wèn)結(jié)果: 如果你的應(yīng)用要對(duì)數(shù)據(jù)庫(kù)進(jìn)行頻繁訪問(wèn)的話,你可以使用一個(gè)對(duì)象來(lái)緩存你的數(shù)據(jù),這樣你就可以節(jié)省大量訪問(wèn)數(shù)據(jù)庫(kù)的開(kāi)銷。在《J2EE核心模式》和《實(shí)用J2EE設(shè)計(jì)模式編程指南》兩本書(shū)中都有關(guān)于值對(duì)象模式(Value Object Pattern)的詳細(xì)探討,你可以參考這兩本書(shū)來(lái)獲得相應(yīng)的知識(shí)。
使用數(shù)據(jù)訪問(wèn)對(duì)象模式:如果你的應(yīng)用需要訪問(wèn)多個(gè)數(shù)據(jù)庫(kù)系統(tǒng)或者可能會(huì)移植到其它的存儲(chǔ)系統(tǒng)中,那么你針對(duì)特定廠商的優(yōu)化代碼就可能會(huì)失效。使用通用的代碼存在執(zhí)行效率的問(wèn)題,而使用優(yōu)化代碼又存在移植的問(wèn)題。所以就產(chǎn)生了數(shù)據(jù)訪問(wèn)對(duì)象模式(Data Access Object Pattern, DAO),該模式既提供了各數(shù)據(jù)庫(kù)廠商的適應(yīng)性,又能利用到他們提供的獨(dú)特的好處。按照面向?qū)ο?/font>的分離任務(wù)的原則,該模式將與企業(yè)信息系統(tǒng)(Enterprise Information System, EIS)通訊需要的邏輯隔離到它自己的類中。這樣,事物對(duì)象,如Servlet/JSP組件、JavaBean就可以利用數(shù)據(jù)訪問(wèn)對(duì)象(DAO)處理所有與EIS有關(guān)的事務(wù)。
最好采用JSP的XML語(yǔ)法: JSP技術(shù)中經(jīng)常存在著兩種完成同一個(gè)任務(wù)的語(yǔ)法,一種是常規(guī)的JSP語(yǔ)法,一種是對(duì)應(yīng)的XML語(yǔ)法。雖然兩種語(yǔ)法作用相同,你最好還是使用XML語(yǔ)法。存在兩種語(yǔ)法的原因是,JSP語(yǔ)法可以與以前的代碼兼容,而J2EE使用XML作為其交換數(shù)據(jù)的核心,所以同時(shí)提供了XML語(yǔ)法。隨著J2EE的發(fā)展,XML的作用會(huì)越來(lái)越大,所以我建議你使用XML語(yǔ)法。
研究Sun提供的J2EE BluePrints: Sun的Enterprise BluePrints 提供了大量指導(dǎo)原則、設(shè)計(jì)模式和很好的例子(寵物店,Pet Store)。你可以好好研究一下這些內(nèi)容,這樣可以提高你的設(shè)計(jì)和開(kāi)發(fā)水平。
整合Servlet和JSP
JSP技術(shù)規(guī)范種給出了兩種使用JSP開(kāi)發(fā)Web應(yīng)用的方式,這兩種方式可以歸納為模型一和模型二,這兩種模型的主要差別在于它們處理業(yè)務(wù)的流程不同。模型一,如下圖所示,稱之為JSP+JavaBeans模型。在這一模型中,JSP頁(yè)面獨(dú)自響應(yīng)請(qǐng)求并將處理結(jié)果返回給客戶,所有的數(shù)據(jù)通過(guò)JavaBean來(lái)處理,JSP實(shí)現(xiàn)頁(yè)面的表現(xiàn)。

圖2: JSP模型一
從上圖可以看出,模型一也實(shí)現(xiàn)了頁(yè)面表現(xiàn)和業(yè)務(wù)邏輯相分離。然而使用這種方式就要在JSP頁(yè)面使用大量的Java代碼,當(dāng)需要處理的業(yè)務(wù)邏輯很復(fù)雜時(shí),這種情況會(huì)變得非常糟糕。大量嵌入式代碼使整個(gè)頁(yè)面程序變得異常復(fù)雜。對(duì)于前端界面設(shè)計(jì)的網(wǎng)頁(yè)開(kāi)發(fā)人員來(lái)說(shuō),這簡(jiǎn)直是一場(chǎng)噩夢(mèng)。所以,模型一不能滿足大型應(yīng)用的需要,但是對(duì)于小型應(yīng)用,因?yàn)樵撃P秃?jiǎn)單,不用涉及諸多要素,從而可以很好地滿足小型應(yīng)用的需要,所以在簡(jiǎn)單應(yīng)用中,可以考慮模型一。
模型二,如下圖所示,稱之為JSP+Servlet+JavaBeans模型。這一模型結(jié)合了JSP和Servlet技術(shù),充分利用了JSP和Servlet兩種技術(shù)原有的優(yōu)勢(shì)。這個(gè)模型使用JSP技術(shù)來(lái)表現(xiàn)頁(yè)面,使用Servlet技術(shù)完成大量的事務(wù)處理,使用Bean來(lái)存儲(chǔ)數(shù)據(jù)。Servlet用來(lái)處理請(qǐng)求的事務(wù),充當(dāng)一個(gè)控制者的角色,并負(fù)責(zé)向客戶發(fā)送請(qǐng)求。它創(chuàng)建JSP需要的Bean和對(duì)象,然后根據(jù)用戶請(qǐng)求的行為,決定將哪個(gè)JSP頁(yè)面發(fā)送給客戶。

圖3: JSP模型二
從開(kāi)發(fā)的觀點(diǎn)看,模型二具有更清晰的頁(yè)面表現(xiàn),清楚的開(kāi)發(fā)角色的劃分,可以充分利用開(kāi)發(fā)團(tuán)隊(duì)中的網(wǎng)頁(yè)設(shè)計(jì)人員和Java開(kāi)發(fā)人員。這些優(yōu)勢(shì)在大型項(xiàng)目中表現(xiàn)得尤為突出,網(wǎng)頁(yè)設(shè)計(jì)人員可以充分發(fā)揮自己的美術(shù)和設(shè)計(jì)才能來(lái)充分表現(xiàn)頁(yè)面,程序編寫人員可以充分發(fā)揮自己的業(yè)務(wù)邏輯處理思維,實(shí)現(xiàn)項(xiàng)目中的業(yè)務(wù)處理。
另外,從設(shè)計(jì)結(jié)構(gòu)來(lái)看,這種模型充分體現(xiàn)了模型視圖控制器(MVC)的設(shè)計(jì)架構(gòu)。事實(shí)上,現(xiàn)存的很多開(kāi)發(fā)框架都是基于這種模型的,充分實(shí)現(xiàn)了MVC ,例如Apache Struts框架和JavaServer Faces框架
如果你已經(jīng)基本了解JSP和Servlet的各項(xiàng)技術(shù)(最好也開(kāi)發(fā)過(guò)一些Web應(yīng)用),那么我們可以一起探討一下如何開(kāi)發(fā)“好”的應(yīng)用的一些指導(dǎo)原則。我們首先對(duì)Servlet和JSP技術(shù)做一個(gè)瀏覽。
Servlet和JSP概覽
早期的動(dòng)態(tài)網(wǎng)頁(yè)主要采用CGI(Common Gateway Interface,公共網(wǎng)關(guān)接口)技術(shù),你可以使用不同的語(yǔ)言編寫CGI程序,如VB、C/C++或Delphi等。雖然CGI技術(shù)發(fā)展成熟且功能強(qiáng)大,但由于編程困難、效率低下、修改復(fù)雜等缺點(diǎn),所以有逐漸被取代的趨勢(shì)。在所有的新技術(shù)中,JSP/Servlet具備更高效、更容易編程、功能更強(qiáng)、更安全和具有良好的可移植性,因而被許多人認(rèn)為是未來(lái)最有發(fā)展前途的動(dòng)態(tài)網(wǎng)站技術(shù)。
與CGI相似,Servlet支持請(qǐng)求/響應(yīng)模型。當(dāng)一個(gè)客戶向服務(wù)器遞交一個(gè)請(qǐng)求時(shí),服務(wù)器把請(qǐng)求送給Servlet,Servlet負(fù)責(zé)處理請(qǐng)求并生成響應(yīng),然后送給服務(wù)器,再由服務(wù)器發(fā)送給客戶。與CGI不同的是,Servlet沒(méi)有生成新的進(jìn)程,而是與HTTP Server處于同一進(jìn)程中。它通過(guò)使用線程技術(shù),減小了服務(wù)器的開(kāi)銷。Servlet處理請(qǐng)求的過(guò)程是這樣的:當(dāng)收到來(lái)自客戶端的請(qǐng)求后,調(diào)用service方法,該方法中Servlet先判斷到來(lái)的請(qǐng)求是什么類型的(GET/POST/HEAD…),然后調(diào)用相應(yīng)的處理方法(doGet/doPost/doHead…)并生成響應(yīng)。
別看這么復(fù)雜,其實(shí)簡(jiǎn)單說(shuō)來(lái)Servlet就是一個(gè)Java類。與一般類的不同之處是,這個(gè)類運(yùn)行在一個(gè)Servlet容器內(nèi),可以提供session管理和對(duì)象生命周期管理。因而當(dāng)你使用Servlet的時(shí)候,你可以得到Java平臺(tái)的所有好處,包括安全性管理、使用JDBC訪問(wèn)數(shù)據(jù)庫(kù)以及跨平臺(tái)的能力。而且,Servlet使用線程,因而可以開(kāi)發(fā)出效率更高的Web應(yīng)用。
JavaServer Pages (JSP)
JSP技術(shù)是J2EE的一個(gè)關(guān)鍵技術(shù),它在更高一級(jí)的層次上抽象Servlet。它可以讓常規(guī)靜態(tài)HTML與動(dòng)態(tài)產(chǎn)生的內(nèi)容相結(jié)合,看起來(lái)像一個(gè)HTML網(wǎng)頁(yè),卻作為Servlet來(lái)運(yùn)行。現(xiàn)在有許多商業(yè)應(yīng)用服務(wù)器支持JSP技術(shù),比如BEA WebLogic、IBM WebSphere、 JRun等等。使用JSP比用Servlet更簡(jiǎn)單。如果你有一個(gè)支持JSP的Web服務(wù)器,并且有一個(gè)JSP文件,你可以把它放倒任何靜態(tài)HTML文件可以放置的位置,不用編譯,不用打包,也不用進(jìn)行ClassPath的設(shè)置,就可以像訪問(wèn)普通網(wǎng)頁(yè)那樣訪問(wèn)它,服務(wù)器會(huì)自動(dòng)幫你做好其他的工作。
JSP工作原理
JSP 文件看起來(lái)就像一個(gè)普通靜態(tài)HTML文件,只不過(guò)里面包含了一些Java代碼。它使用.jsp的后綴,用來(lái)告訴服務(wù)器這個(gè)文件需要特殊的處理。當(dāng)我們?cè)L問(wèn)一個(gè)JSP頁(yè)面的時(shí)候,這個(gè)文件首先會(huì)被JSP引擎翻譯為一個(gè)Java源文件,其實(shí)就是一個(gè)Servlet,并進(jìn)行編譯,然后像其他Servlet一樣,由Servlet引擎來(lái)處理。Servlet引擎裝載這個(gè)類,處理來(lái)自客戶的請(qǐng)求,并把結(jié)果返回給客戶,如下圖所示:

圖1: 調(diào)用JSP頁(yè)面的流程
以后再有客戶訪問(wèn)這個(gè)頁(yè)面的時(shí)候,只要該文件沒(méi)有發(fā)生過(guò)更改,JSP引擎就直接調(diào)用已經(jīng)裝載的Servlet。如果已經(jīng)做過(guò)修改的話,那就會(huì)再次執(zhí)行以上過(guò)程,翻譯、編譯并裝載。其實(shí)這就是所謂的“第一人懲罰”。因?yàn)槭状卧L問(wèn)的時(shí)候要執(zhí)行一系列以上的過(guò)程,所以會(huì)耗費(fèi)一些時(shí)間;以后的訪問(wèn)就不會(huì)這樣了。
開(kāi)發(fā)原則
這一部分我們列出一些開(kāi)發(fā)原則,重點(diǎn)是JSP頁(yè)面。關(guān)于如何分離表現(xiàn)和內(nèi)容的MVC因?yàn)橐婕暗?a class=channel_keylink >JSP和Servlet的整合,我們稍候再談。
不要在JSP頁(yè)面中嵌入過(guò)量的Java代碼:對(duì)于非常簡(jiǎn)單或是測(cè)試性的代碼,把所有的Java 代碼直接放入JSP頁(yè)面內(nèi)是沒(méi)有問(wèn)題的。但是這種方法不應(yīng)該被過(guò)度使用,否則就會(huì)產(chǎn)生一大堆HTML和Java混合起來(lái)的代碼,讓人難以閱讀和理解。解決方法是寫一個(gè)單獨(dú)的類,用來(lái)執(zhí)行相關(guān)的計(jì)算。一旦這個(gè)類測(cè)試通過(guò),就可以把它放在任何執(zhí)行同樣計(jì)算的場(chǎng)合中。這樣可以促進(jìn)代碼的復(fù)用。
選擇合適的包含(include)機(jī)制: 如果一個(gè)應(yīng)用中每個(gè)頁(yè)面有一樣的抬頭和底部,或者還有導(dǎo)航條,那么就應(yīng)該把它們放到一個(gè)單獨(dú)的文件中,然后在每一個(gè)頁(yè)面中使用包含機(jī)制把它們加入到這個(gè)頁(yè)面中:
Include 指令: <%@ include file="filename" %>或等效xml語(yǔ)法
<jsp:directive.includefile=”filename” />
Include 動(dòng)作: <jsp:include page="page.jsp" flush="true" />
Include指令是當(dāng)JSP頁(yè)面翻譯為Servlet的時(shí)候包含另外一個(gè)文件,Include 動(dòng)作是當(dāng)請(qǐng)求時(shí)包含另外一個(gè)文件的輸出。如果被包含的文件不是經(jīng)常改動(dòng)的時(shí)候,我建議使用Include 指令,這樣速度更快。如果被包含的文件需要不時(shí)改動(dòng)或者知道請(qǐng)求時(shí)才能決定需要包含的內(nèi)容時(shí),那么應(yīng)該使用Include 動(dòng)作。
如果你使用JSP標(biāo)準(zhǔn)標(biāo)記庫(kù)(JavaServer pages Standard Tag Library即JSTL)的話,那么還有第三中包含機(jī)制<c:import>,可以用來(lái)包含本地或者遠(yuǎn)程的資源。例如:
<c:import url="./copyright.html"/>
<c:import url="http://www.somewhere.com/hello.xml"/>
不要把業(yè)務(wù)邏輯和表示混合在一起: 復(fù)雜的應(yīng)用涉及大量的代碼,因而把業(yè)務(wù)邏輯和前端的表示相分離就顯得格外重要,這種分離可以讓任何一方的變化不會(huì)影響到另外一方。所以,所有的JSP代碼都應(yīng)該限制在表示層,可是如果這樣的話,你如何實(shí)現(xiàn)你的業(yè)務(wù)邏輯呢?這就是JavaBean所做的事情。JavaBean技術(shù)是一個(gè)獨(dú)立于平臺(tái)的組件模型,它讓開(kāi)發(fā)者編寫、測(cè)試通過(guò)一個(gè)組件后,可以隨處使用,提高了復(fù)用性。在JSP技術(shù)中,JavaBean實(shí)現(xiàn)了業(yè)務(wù)邏輯部分,它把數(shù)據(jù)返回給JSP頁(yè)面,由JSP頁(yè)面負(fù)責(zé)格式化數(shù)據(jù)并輸出到客戶端的瀏覽器。在JSP頁(yè)面中使用JavaBean組件的好處是:
產(chǎn)生了可以復(fù)用的組件:任何應(yīng)用都可以使用這些組件
可以把業(yè)務(wù)邏輯和表示相分離:你可以修改數(shù)據(jù)的顯示方式而不用考慮業(yè)務(wù)邏輯。這樣做的結(jié)果也可以明確工作中開(kāi)發(fā)人員的分工,網(wǎng)頁(yè)開(kāi)發(fā)人員可以把精力放到如何顯示數(shù)據(jù)上,Java開(kāi)發(fā)者則更為關(guān)注業(yè)務(wù)邏輯的實(shí)現(xiàn)。
對(duì)于JavaBean你不用提供源代碼,這樣你的代碼就不會(huì)被瀏覽器網(wǎng)頁(yè)的人輕易獲得,可以保護(hù)你的勞動(dòng)成果。
如果你的應(yīng)用中使用了EJB組件,那么業(yè)務(wù)邏輯就應(yīng)該放置在EJB中,因?yàn)镋JB模型提供了生命周期管理、事務(wù)管理以及多客戶訪問(wèn)域?qū)ο螅‥ntity Beans)。你可以仔細(xì)看一下Enterprise BluePrints中的例子,它就是這么做的。
使用定制的標(biāo)記: 上面我們已經(jīng)討論過(guò),把所有Java代碼嵌入到JSP頁(yè)面內(nèi)并不合適,因?yàn)榫W(wǎng)頁(yè)開(kāi)發(fā)人員并不一定懂得Java語(yǔ)言,更難以理解Java語(yǔ)法。JavaBean可以封裝很多Java代碼,不過(guò)在JSP頁(yè)面內(nèi)使用JavaBean仍然要求頁(yè)面開(kāi)發(fā)人員了解一些Java語(yǔ)法。
JSP技術(shù)中包含了定制標(biāo)記庫(kù)的功能。Java開(kāi)發(fā)人員可以生成自己的標(biāo)記庫(kù),這樣網(wǎng)頁(yè)設(shè)計(jì)人員就可以使用類似HTML的語(yǔ)法來(lái)使用這些標(biāo)記。編寫和使用自己定制的標(biāo)記庫(kù)可以在更大程度上促進(jìn)業(yè)務(wù)邏輯和表示的分離。使用定制標(biāo)記庫(kù)主要有以下好處:
可以消除在JSP頁(yè)面中使用scriptlet 標(biāo)記使用的任何參數(shù)都可以通過(guò)屬性傳入,從而不需要使用Java代碼就可以達(dá)到希望的目的。
可以簡(jiǎn)化使用 網(wǎng)頁(yè)設(shè)計(jì)人員不需要學(xué)會(huì)使用Java語(yǔ)法,他們可以用類似HTML語(yǔ)法就可以使用標(biāo)記。
不懂Java的網(wǎng)頁(yè)設(shè)計(jì)人員可以使用標(biāo)記庫(kù)來(lái)完成單獨(dú)使用HTML不能完成的任務(wù)。
提高了復(fù)用性 標(biāo)記庫(kù)完全可以復(fù)用,這可以節(jié)省開(kāi)發(fā)和測(cè)試的時(shí)間。Scriptlet代碼只能在“拷貝粘貼”級(jí)別上進(jìn)行“復(fù)用”。
簡(jiǎn)單說(shuō)來(lái),你可以像用HTML構(gòu)建表示層一樣使用標(biāo)記庫(kù)完成非常復(fù)雜的任務(wù)。下面是表頁(yè)標(biāo)記庫(kù)的一些注意事項(xiàng):
1. 保持簡(jiǎn)潔性:如果一個(gè)標(biāo)記需要好幾個(gè)屬性的話,那么盡可能把它分為幾個(gè)標(biāo)記。
2. 保持復(fù)用性:同標(biāo)記的使用人員(網(wǎng)頁(yè)設(shè)計(jì)人員)多多交流,盡量開(kāi)發(fā)出可以高度復(fù)用的標(biāo)記庫(kù)。
3. 不要一切都從頭開(kāi)始:現(xiàn)在已經(jīng)有一些可以免費(fèi)使用的標(biāo)記庫(kù),比如Jakarta Taglibs。如果你要用到一個(gè)標(biāo)記,先看看是不是已經(jīng)有現(xiàn)成的可以使用。
不要“重新發(fā)明輪子”,不要一切從頭開(kāi)始: 通過(guò)定制組件可以提高復(fù)用性,不過(guò)定制組件仍然需要編寫、測(cè)試和調(diào)試程序。問(wèn)題是這個(gè)事情別人可能已經(jīng)實(shí)現(xiàn)了,而且你的實(shí)現(xiàn)方式并不一定比人家做得更好。這就是JSP標(biāo)準(zhǔn)標(biāo)記庫(kù)(JavaServer Pages Standard Tag Library, JSTL)要做的事情(JSTL請(qǐng)參考JSTL官方網(wǎng)站)。JSTL提供了循環(huán)、讀屬性、遍歷各種數(shù)據(jù)結(jié)構(gòu)、條件表達(dá)式求值等各種標(biāo)記。它也提供了一些復(fù)雜的標(biāo)記,甚至像解析XML文檔的標(biāo)記它都有。所以如果你要用到一個(gè)標(biāo)記的話,最好先看看有沒(méi)有別人已經(jīng)實(shí)現(xiàn)的可以使用,而不要次次從頭開(kāi)始,自己搞一套。
使用JSTL表達(dá)使語(yǔ)言(JSTL Expression Language): 傳遞給JSP頁(yè)面的數(shù)據(jù)一般通過(guò)JSP作用域屬性或者請(qǐng)求參數(shù)來(lái)進(jìn)行。專門為網(wǎng)頁(yè)開(kāi)發(fā)者設(shè)計(jì)的表達(dá)式語(yǔ)言(Expression Language, EL)把使用作用域屬性傳遞信息作為從業(yè)務(wù)邏輯向JSP頁(yè)面?zhèn)鬟f信息的標(biāo)準(zhǔn)方式。這里要注意的是,EL只是JSP技術(shù)中關(guān)鍵的一個(gè)方面,并不是一種通用的程序設(shè)計(jì)語(yǔ)言。相反,它只是一種數(shù)據(jù)訪問(wèn)語(yǔ)言,它可以簡(jiǎn)化應(yīng)用程序的數(shù)據(jù)的訪問(wèn),不用Scriptlet和請(qǐng)求時(shí)表達(dá)式求值就可以訪問(wèn)數(shù)據(jù)。
在JSP中,網(wǎng)頁(yè)設(shè)計(jì)師要使用表達(dá)式語(yǔ)法<%= name %>或JavaBean組件來(lái)取得某些變量或屬性的值,例如:
<tagLib:tag attribute="<%=
pageContext.getAttribute("name") %>">
或
<%= aCustomerBean.getAddress().getCountry() %>
表達(dá)使語(yǔ)言讓網(wǎng)頁(yè)設(shè)計(jì)師可以使用簡(jiǎn)化的語(yǔ)法來(lái)訪問(wèn)信息。如果你只是要訪問(wèn)一個(gè)簡(jiǎn)單的變量,你可以使用這樣的語(yǔ)法:
<tagLib:tag attribute="${name}">
如果你要訪問(wèn)一個(gè)嵌套JavaBean的屬性,你可以這樣:
<tagLib:tag attribute ="${
aCustomerBean.address.country}">
表達(dá)式語(yǔ)言(EL)借用了JavaScript 的語(yǔ)法,所以如果你對(duì)JavaScript 很熟悉的話,你就會(huì)覺(jué)得巨爽。
使用過(guò)濾器(filter): 過(guò)濾器是一個(gè)對(duì)象,可以傳輸請(qǐng)求或修改響應(yīng)。它可以在請(qǐng)求到達(dá)Servlet/JSP之前對(duì)其進(jìn)行預(yù)處理,而且能夠在響應(yīng)離開(kāi)Servlet/JSP之后對(duì)其進(jìn)行后處理。所以如果你有幾個(gè)Servlet/JSP需要執(zhí)行同樣的數(shù)據(jù)轉(zhuǎn)換或頁(yè)面處理的話,你就可以寫一個(gè)過(guò)濾器類,然后在部署描述文件(web.xml)中把該過(guò)濾器與對(duì)應(yīng)的Servlet/JSP聯(lián)系起來(lái)。
創(chuàng)建過(guò)濾器其實(shí)很容易,你只須實(shí)現(xiàn)javax.servlet.Filter接口及它的三個(gè)方法:
public void init(FilterConfig config)
public void doFilter(ServletRequest req, ServletResponse rep,
FilterChain chain)
public void destroy()
這樣,你就可以完成你的過(guò)濾器。
使用可移植的安全模型:大部分的應(yīng)用服務(wù)器都提供了安全模型,不過(guò)一般它們都是針對(duì)某一個(gè)服務(wù)器或某一個(gè)廠商專有的。如果你的應(yīng)用需要移植的話,那么你的應(yīng)用最好使用可以移植的安全模型。如果你的應(yīng)用有一些預(yù)先定義的固定用戶的話,那么你可以使用FROM驗(yàn)證和BASIC驗(yàn)證。可是如果你要?jiǎng)討B(tài)生成客戶的話(一般都是這種情況),你可能就需要使用服務(wù)器特定的API來(lái)創(chuàng)建和管理用戶。這樣當(dāng)你的應(yīng)用移植到另外一個(gè)服務(wù)器時(shí),你可能就會(huì)碰到API不兼容的問(wèn)題。這種情況下,最好的解決方法是使用適配器(Adapter)模式(如果你對(duì)設(shè)計(jì)模式不熟悉的話,請(qǐng)參看GoF的《設(shè)計(jì)模式》一書(shū))。
用數(shù)據(jù)庫(kù)來(lái)保存持久性數(shù)據(jù): Servlet/JSP中可以使用HttpSession對(duì)象也就是會(huì)話對(duì)象來(lái)保存用戶的臨時(shí)數(shù)據(jù)。不過(guò)如果你想保存持久性數(shù)據(jù)的時(shí)候,你應(yīng)該使用數(shù)據(jù)庫(kù),數(shù)據(jù)保存數(shù)據(jù)會(huì)更安全,而且對(duì)客戶所用的瀏覽器沒(méi)有什么要求。這樣即使你的應(yīng)用服務(wù)器由于某種原因崩潰了,你的數(shù)據(jù)依然良好。
高速緩存頁(yè)面: 應(yīng)用程序中總有一些東西是相對(duì)固定的,而另外一些東西是經(jīng)常變化的。你應(yīng)該使用靜態(tài)的HTML文檔來(lái)存儲(chǔ)那些相對(duì)固定的內(nèi)容,這樣客戶端就可以進(jìn)行高速緩存,客戶每次訪問(wèn)你的應(yīng)用時(shí),只需訪問(wèn)已經(jīng)改動(dòng)的部分。這樣可以加快客戶的訪問(wèn)速度。
使用連接池: 如果你要自己寫數(shù)據(jù)庫(kù)訪問(wèn)代碼的話,我覺(jué)得使用你應(yīng)該學(xué)會(huì)如何使用數(shù)據(jù)庫(kù)連接池技術(shù)。每一個(gè)服務(wù)器都有針對(duì)數(shù)據(jù)庫(kù)連接池的配置文檔,你要學(xué)習(xí)一下如何使用。數(shù)據(jù)庫(kù)連接池可以加速你的應(yīng)用的數(shù)據(jù)訪問(wèn)的速度,而且由于服務(wù)器替你管理了數(shù)據(jù)庫(kù)連接,這可以節(jié)省你的很多工作。
緩存數(shù)據(jù)庫(kù)的訪問(wèn)結(jié)果: 如果你的應(yīng)用要對(duì)數(shù)據(jù)庫(kù)進(jìn)行頻繁訪問(wèn)的話,你可以使用一個(gè)對(duì)象來(lái)緩存你的數(shù)據(jù),這樣你就可以節(jié)省大量訪問(wèn)數(shù)據(jù)庫(kù)的開(kāi)銷。在《J2EE核心模式》和《實(shí)用J2EE設(shè)計(jì)模式編程指南》兩本書(shū)中都有關(guān)于值對(duì)象模式(Value Object Pattern)的詳細(xì)探討,你可以參考這兩本書(shū)來(lái)獲得相應(yīng)的知識(shí)。
使用數(shù)據(jù)訪問(wèn)對(duì)象模式:如果你的應(yīng)用需要訪問(wèn)多個(gè)數(shù)據(jù)庫(kù)系統(tǒng)或者可能會(huì)移植到其它的存儲(chǔ)系統(tǒng)中,那么你針對(duì)特定廠商的優(yōu)化代碼就可能會(huì)失效。使用通用的代碼存在執(zhí)行效率的問(wèn)題,而使用優(yōu)化代碼又存在移植的問(wèn)題。所以就產(chǎn)生了數(shù)據(jù)訪問(wèn)對(duì)象模式(Data Access Object Pattern, DAO),該模式既提供了各數(shù)據(jù)庫(kù)廠商的適應(yīng)性,又能利用到他們提供的獨(dú)特的好處。按照面向?qū)ο?/font>的分離任務(wù)的原則,該模式將與企業(yè)信息系統(tǒng)(Enterprise Information System, EIS)通訊需要的邏輯隔離到它自己的類中。這樣,事物對(duì)象,如Servlet/JSP組件、JavaBean就可以利用數(shù)據(jù)訪問(wèn)對(duì)象(DAO)處理所有與EIS有關(guān)的事務(wù)。
最好采用JSP的XML語(yǔ)法: JSP技術(shù)中經(jīng)常存在著兩種完成同一個(gè)任務(wù)的語(yǔ)法,一種是常規(guī)的JSP語(yǔ)法,一種是對(duì)應(yīng)的XML語(yǔ)法。雖然兩種語(yǔ)法作用相同,你最好還是使用XML語(yǔ)法。存在兩種語(yǔ)法的原因是,JSP語(yǔ)法可以與以前的代碼兼容,而J2EE使用XML作為其交換數(shù)據(jù)的核心,所以同時(shí)提供了XML語(yǔ)法。隨著J2EE的發(fā)展,XML的作用會(huì)越來(lái)越大,所以我建議你使用XML語(yǔ)法。
研究Sun提供的J2EE BluePrints: Sun的Enterprise BluePrints 提供了大量指導(dǎo)原則、設(shè)計(jì)模式和很好的例子(寵物店,Pet Store)。你可以好好研究一下這些內(nèi)容,這樣可以提高你的設(shè)計(jì)和開(kāi)發(fā)水平。
整合Servlet和JSP
JSP技術(shù)規(guī)范種給出了兩種使用JSP開(kāi)發(fā)Web應(yīng)用的方式,這兩種方式可以歸納為模型一和模型二,這兩種模型的主要差別在于它們處理業(yè)務(wù)的流程不同。模型一,如下圖所示,稱之為JSP+JavaBeans模型。在這一模型中,JSP頁(yè)面獨(dú)自響應(yīng)請(qǐng)求并將處理結(jié)果返回給客戶,所有的數(shù)據(jù)通過(guò)JavaBean來(lái)處理,JSP實(shí)現(xiàn)頁(yè)面的表現(xiàn)。

圖2: JSP模型一
從上圖可以看出,模型一也實(shí)現(xiàn)了頁(yè)面表現(xiàn)和業(yè)務(wù)邏輯相分離。然而使用這種方式就要在JSP頁(yè)面使用大量的Java代碼,當(dāng)需要處理的業(yè)務(wù)邏輯很復(fù)雜時(shí),這種情況會(huì)變得非常糟糕。大量嵌入式代碼使整個(gè)頁(yè)面程序變得異常復(fù)雜。對(duì)于前端界面設(shè)計(jì)的網(wǎng)頁(yè)開(kāi)發(fā)人員來(lái)說(shuō),這簡(jiǎn)直是一場(chǎng)噩夢(mèng)。所以,模型一不能滿足大型應(yīng)用的需要,但是對(duì)于小型應(yīng)用,因?yàn)樵撃P秃?jiǎn)單,不用涉及諸多要素,從而可以很好地滿足小型應(yīng)用的需要,所以在簡(jiǎn)單應(yīng)用中,可以考慮模型一。
模型二,如下圖所示,稱之為JSP+Servlet+JavaBeans模型。這一模型結(jié)合了JSP和Servlet技術(shù),充分利用了JSP和Servlet兩種技術(shù)原有的優(yōu)勢(shì)。這個(gè)模型使用JSP技術(shù)來(lái)表現(xiàn)頁(yè)面,使用Servlet技術(shù)完成大量的事務(wù)處理,使用Bean來(lái)存儲(chǔ)數(shù)據(jù)。Servlet用來(lái)處理請(qǐng)求的事務(wù),充當(dāng)一個(gè)控制者的角色,并負(fù)責(zé)向客戶發(fā)送請(qǐng)求。它創(chuàng)建JSP需要的Bean和對(duì)象,然后根據(jù)用戶請(qǐng)求的行為,決定將哪個(gè)JSP頁(yè)面發(fā)送給客戶。

圖3: JSP模型二
從開(kāi)發(fā)的觀點(diǎn)看,模型二具有更清晰的頁(yè)面表現(xiàn),清楚的開(kāi)發(fā)角色的劃分,可以充分利用開(kāi)發(fā)團(tuán)隊(duì)中的網(wǎng)頁(yè)設(shè)計(jì)人員和Java開(kāi)發(fā)人員。這些優(yōu)勢(shì)在大型項(xiàng)目中表現(xiàn)得尤為突出,網(wǎng)頁(yè)設(shè)計(jì)人員可以充分發(fā)揮自己的美術(shù)和設(shè)計(jì)才能來(lái)充分表現(xiàn)頁(yè)面,程序編寫人員可以充分發(fā)揮自己的業(yè)務(wù)邏輯處理思維,實(shí)現(xiàn)項(xiàng)目中的業(yè)務(wù)處理。
另外,從設(shè)計(jì)結(jié)構(gòu)來(lái)看,這種模型充分體現(xiàn)了模型視圖控制器(MVC)的設(shè)計(jì)架構(gòu)。事實(shí)上,現(xiàn)存的很多開(kāi)發(fā)框架都是基于這種模型的,充分實(shí)現(xiàn)了MVC ,例如Apache Struts框架和JavaServer Faces框架