即將面世的j2ee 1.4提供用java開發(fā)web應(yīng)用程序的新的servlet 2.4和javaserver pages (jsp) 2.0技術(shù)。本文展示了這兩種技術(shù)的新特性,并在適當(dāng)?shù)牡胤教峁┟總€(gè)特性的示例代碼。本文假設(shè)讀者熟悉以前的 servlet 2.3和jsp 1.2版本。給出的例子已用tomcat 5(包含在java web services developer pack 1.2中)進(jìn)行了測試。
servlet和jsp毫無疑問是兩種應(yīng)用最廣的j2ee技術(shù)。servlet技術(shù)是用java進(jìn)行web應(yīng)用編程的基礎(chǔ),也是jsp的基礎(chǔ)。但是,servlet編程可能會(huì)非常麻煩。特別是當(dāng)你不得不發(fā)送一個(gè)沒多少代碼的長html頁面時(shí)更是如此。每個(gè)html標(biāo)記必須嵌入到字符串中,用printwriter對象的顯示方式發(fā)送。是一種工作單調(diào)乏味而煩人的工作。使用servlet的另一個(gè)缺點(diǎn)是每一處改變都需要servlet程序員介入。
sun公司了解到這一問題之后便開發(fā)了jsp作為解決方案。在jsp中,程序員和頁面設(shè)計(jì)員的分工變得容易多了,并且當(dāng)jsp頁面更改時(shí)會(huì)自動(dòng)進(jìn)行編譯。不過請注意,jsp是servlet技術(shù)的一個(gè)擴(kuò)展,而不是廢棄servlet。在實(shí)際應(yīng)用當(dāng)中,servlet和jsp頁面一起使用。
servlet 2.4的新特性
servlet 2.4提供了幾個(gè)新類,且不支持javax.servlet.singlethreadmodel接口。這一版本只支持http 1.1,所以servlet 2.4應(yīng)用程序不適用于http 1.0客戶程序。2.4版增加了請求監(jiān)聽器和請求屬性監(jiān)聽器,并能在一個(gè)應(yīng)用程序中將servlet用作歡迎頁面。另外,servlet 2.4還提供了更好的servletrequest和requestdispatcher對象,并更好地支持國際化。此外,現(xiàn)在是根據(jù)模式而不是文檔類型定義(document-type definition,dtd)文件來驗(yàn)證部署描述符是否有效。這就意味著支持部署描述符的可擴(kuò)展性。
下面具體說明servlet 2.4的新特性。請求監(jiān)聽器和請求屬性監(jiān)聽器。servlet 2.3增加了servlet上下文相關(guān)監(jiān)聽器和會(huì)話相關(guān)監(jiān)聽器。servlet 2.4增加了新的javax.servlet.servletrequestlistener和javax.servlet.servletrequestattributelistener兩種接口,它們會(huì)通知你與request對象有關(guān)的事件。如果你對每個(gè)request對象的初始化和撤消感興趣,你可以實(shí)施servletrequestlistener接口。這個(gè)接口有兩個(gè)方法:requestinitialized()和requestdestroyed()。當(dāng)需要一個(gè)request對象時(shí),servlet容器便調(diào)用requestinitialized方法。當(dāng)不再需要request對象時(shí),servlet容器便調(diào)用requestdestroyed方法。
這兩個(gè)方法都從servlet容器接收一個(gè)javax.servlet.servletrequestevent對象。可以從servletrequestevent實(shí)例獲得servlet上下文和servlet請求。
第二個(gè)監(jiān)聽器接口servletrequestattributelistener處理request對象屬性的添加、更改和刪除。該接口有以下方法:
這三個(gè)方法從servlet容器獲得javax.servlet.servletrequestattributeevent類的一個(gè)實(shí)例。servletrequestattributeevent類擴(kuò)展了servletrequestevent類,并添加了兩個(gè)新方法:getname和getvalue。getname方法返回觸發(fā)事件的屬性的名稱,getvalue返回屬性的值。
代碼清單1 給出這兩個(gè)新的監(jiān)聽器的示例類。當(dāng)servlet容器調(diào)用方法時(shí)二者都顯示方法名。監(jiān)聽器經(jīng)過編譯后,它們的類文件必須被部署到web-inf/classes目錄下。servletrequest中的新方法。在servlet 2.4中,javax.servlet.servletrequest接口增加了4個(gè)新方法:
請注意,在servlet 2.3中,getservername和getserverport方法返回的值就是現(xiàn)在getlocalname和getlocalport返回的值。在2.4版中,getservername和getserverport已重新定義。欲了解更多的信息,請查看api文檔。
將一個(gè)jsp頁面中的代碼示例如下--
out.println("<br>remote port : " + request.getremoteport());out.println("<br>local name : " + request.getlocalname());out.println("<br>local addr : " + request.getlocaladdr());out.println("<br>local port : " + request.getlocalport());
--該代碼生成這樣的內(nèi)容:
remote port : 3303 local name : localhost local addr : 127.0.0.1 local port : 8080
請求調(diào)度程序的新特性。使用請求調(diào)度程序可將當(dāng)前請求傳遞給一個(gè)新的資源,或從當(dāng)前頁面引入另一個(gè)資源。servlet 2.4增加了一些屬性,它們將被添加到傳遞給另一個(gè)資源的一個(gè)request對象上:
javax.servlet.forward.request_urijavax.servlet.forward.context_pathjavax.servlet.forward.servlet_pathjavax.servlet.forward.path_infojavax.servlet.forward.query_string
如果一個(gè)request對象未被傳遞,則這些屬性的值為null。另一方面,在所傳遞來對象的資源中這些屬性將具有非null值。當(dāng)某一個(gè)資源必須只能通過另一個(gè)資源調(diào)用而不能直接調(diào)用時(shí),這些屬性值很有用。
舉個(gè)例子,在一個(gè)叫做myapp的context(上下文)中有一個(gè)名為modernservlet的servlet, modernservlet被傳遞給targetservlet。 在targetservlet中,顯示代碼清單2中的代碼。
myapp的部署描述符包含以下
<servlet> <servlet-name>modern</servlet-name> <servlet-class>modernservlet </servlet-class></servlet><servlet-mapping> <servlet-name>modern</servlet-name> <url-pattern>/modern</url-pattern> </servlet-mapping><servlet> <servlet-name>target</servlet-name> <servlet-class>targetservlet </servlet-class></servlet><servlet-mapping> <servlet-name>target</servlet-name> <url-pattern>/target</url-pattern></servlet-mapping>
下面是調(diào)用modernservlet時(shí)控制臺(tái)顯示的結(jié)果:
javax.servlet.forward.request_uri : /myapp/modernjavax.servlet.forward.context_path : /myappjavax.servlet.forward.servlet_path : /modernjavax.servlet.forward.path_info : nulljavax.servlet.forward.query_string : null
將過濾器用于請求調(diào)度程序。servlet 2.4在部署描述符中添加了一個(gè)新的
servlet 2.4只支持http 1.1客戶機(jī)。servlet 2.3既支持http 1.0,又支持http 1.1,而servlet 2.4與servlet 2.3不同,它只支持http 1.1客戶機(jī)。作為過渡,http/1.0狀態(tài)碼302(暫時(shí)建議)仍然存在,而且仍然由javax.servlet.http.httpservletresponse接口中的sc_moved_temporarily表示。http 1.1具有found的狀態(tài)碼302,它由httpservletresponse接口中的靜態(tài)sc_found表示。
servlet用作歡迎頁面。在servlet 2.3中,你可以在部署描述符中使用
<servlet> <servlet-name>modern</servlet-name> <servlet-class>modernservlet </servlet-class></servlet><servlet-mapping> <servlet-name>modern</servlet-name> <url-pattern>/modern</url-pattern></servlet-mapping><welcome-file-list> <welcome-file>modern</welcome-file></welcome-file-list>
此時(shí),若用戶鍵入諸如http://domain/context/(不帶資源文件)的url時(shí),就會(huì)調(diào)用modernservlet。
對國際化的新支持。在servlet 2.3中,沒有辦法直接告訴客戶瀏覽器應(yīng)當(dāng)使用什么字符編碼。要實(shí)現(xiàn)這一目的,你必須把一個(gè)java.util.locale對象傳遞給javax.servlet.servletresponse接口的setlocale方法,如下所示:
response.setlocale(locale);
這意味著你必須首先創(chuàng)建一個(gè)locale對象。
另外一種辦法是,在servlet 2.3中,你可以使用setcontenttype方法來傳遞內(nèi)容類型和字符集,如:
setcontenttype('text/html; charset=utf-8');
在servlet 2.4中,javax.servlet.servletresponse接口中有兩個(gè)支持國際化的新方法。第一個(gè)方法是setcharacterencoding,它的用法如下:
public voidsetcharacterencoding(string charset)
使用setcharacterencoding,你可以只將字符編碼指定為一個(gè)字符串,而不必先創(chuàng)建locale對象。不過,請注意,要讓這種方法起作用,必須在調(diào)用getwriter方法之前以及響應(yīng)提交之前調(diào)用它。
第二個(gè)新方法是getcontexttype,作為在servletresponse對象中調(diào)用setcontenttype、setlocale或setcharacterencoding方法的結(jié)果,它返回在servletresponse對象中使用的內(nèi)容類型。
除了javax.servlet.servletresponse中的這兩個(gè)方法之外,你還可以利用servlet 2.4在部署描述符中定義一個(gè)新元素:
<locale-encoding-mapping-list> <locale-encoding-mapping> <locale>ja</locale> <encoding>iso-2022-jp</encoding> </locale-encoding-mapping></locale-encoding-mapping-list>
部署描述符的可擴(kuò)展性。在servlet 2.3應(yīng)用程序中,根據(jù)dtd文件對部署描述符進(jìn)行驗(yàn)證。現(xiàn)在servlet 2.4支持根據(jù)模式對部署描述符進(jìn)行驗(yàn)證。使用模式比使用dtd有以下幾點(diǎn)好處:
但是,為了向后兼容,要求servlet 2.4容器支持servlet 2.3和servlet 2.2 dtd。
不支持javax.servlet.singlethreadmodel接口。singlethreadmodel接口沒有方法,它用于向servlet容器指明,它必須保證不會(huì)有兩個(gè)線程同時(shí)執(zhí)行實(shí)施該接口的servlet的服務(wù)方法。從servlet技術(shù)開始出現(xiàn)到現(xiàn)在,人們普遍誤解了這個(gè)接口。現(xiàn)在大家都反對用它,因?yàn)樗鼤?huì)造成混亂,并且在考慮線程安全時(shí)在安全性方面給servlet程序員一個(gè)錯(cuò)覺。在任何新的開發(fā)工作中決不應(yīng)再使用這個(gè)接口。
jsp 2.0中的新特性
jsp 2.0(最初稱為jsp 1.3)比jsp 1.2有了重要改進(jìn)。當(dāng)然,增加的最重要內(nèi)容是jsp 2.0容器中加入了對表達(dá)式語言(el)的支持。
el最初是由jsp標(biāo)準(zhǔn)標(biāo)記庫(jstl)1.0規(guī)范定義的,它可協(xié)助從jsp頁面中刪除java代碼。javax.servlet.jsp.el包中所描述的api揭示el的語義。el表達(dá)式的語義與java表達(dá)式的語義類似;表達(dá)式的值計(jì)算出來后被插入到當(dāng)前的輸出中。el可用于標(biāo)準(zhǔn)的或定制的操作的屬性值以及模板文本中。下面是el表達(dá)式的結(jié)構(gòu)(其中expr為表達(dá)式):
${expr}
對于包含字符序列"${"的文字值,jsp 2.0提供了一種方法,通過使用序列"${'${'"進(jìn)行換碼。例如,下面的字符序列被轉(zhuǎn)換為文字值${expr}:
${'${'}expr}
此外,由于jsp 2.0以前的版本不支持el,所以jsp應(yīng)用程序?qū)⒑雎匀魏蝫eb應(yīng)用程序中的el,這些應(yīng)用程序的web.xml根據(jù)servlet 2.2或servlet 2.3 dtd進(jìn)行驗(yàn)證。為了測試此處講到的jsp頁面中的表達(dá)式,你只需從應(yīng)用程序中刪除web.xml文件。
實(shí)際上,el是一種簡單的語言,它幫助頁面創(chuàng)作者訪問jsp隱含對象,進(jìn)行反復(fù)操作以及不包含java代碼的條件操作--這些在jsp 1.2中是無法實(shí)現(xiàn)的。
為了訪問隱含對象,jsp容器支持下面的名稱-對象映射:
例如,下面的表達(dá)式表示參數(shù)username的值:
${param.username}
下面的表達(dá)式返回session對象的productid屬性的值:
${sessionscope.productid}
更簡單的simpletag接口操作過程。jsp 2.0提供了一個(gè)新的接口javax.servlet.jsp.tagext.simpletag,它是編寫標(biāo)記處理器(tag handler)的一種更簡單的方法。在jsp 1.2中,標(biāo)記處理器必須直接或間接地實(shí)施avax.servlet.jsp.tagext包中的下列接口之一:tag、iterationtag或bodytag。對于實(shí)施tag接口的標(biāo)記處理器來說,最基本的情況是,jsp容器每次遇到j(luò)sp頁面中的一個(gè)標(biāo)記時(shí)就調(diào)用dostarttag和doendtag兩個(gè)方法。利用jsp 2.0,jsp程序員可以通過實(shí)施新的simpletag接口來選擇實(shí)施過程更簡單的標(biāo)記處理器。jsp容器并不調(diào)用實(shí)施tag接口的標(biāo)記處理器的兩個(gè)方法,而只需要調(diào)用simpletag接口中的一個(gè)方法:dotag。所有標(biāo)記邏輯、反復(fù)操作和主體評(píng)估等都用這一個(gè)方法來執(zhí)行。所以,simpletag與javax.servlet.jsp.tagext.bodytag功能一樣強(qiáng)大,但操作過程更簡單。
為了支持需要實(shí)施simpletag接口的標(biāo)記處理器的編寫,javax.servlet.jsp.tagext包提供了一個(gè)名為simpletagsupport的支持類。如果你要擴(kuò)展這個(gè)類,則你只需提供一個(gè)執(zhí)行方法:dotag。
代碼清單3給出了一個(gè)擴(kuò)展simpletagsupport的標(biāo)記處理器的例子。
使用標(biāo)記文件更輕松地開發(fā)標(biāo)記庫。眾所周知,jsp 1.2中的自定義標(biāo)記庫需要花很多時(shí)間來開發(fā)。開發(fā)工作涉及標(biāo)記處理器和標(biāo)記庫描述符(tld)文件的開發(fā),以及標(biāo)記庫在web.xml文件中的注冊。jsp 2.0通過提供一種新的編寫自定義標(biāo)記庫的方法解決了這個(gè)問題。使用標(biāo)記文件,標(biāo)記擴(kuò)展可類似于jsp文件。無需編譯,無需編輯web.xml文件,而且不再需要tld。要做的是你必須把標(biāo)記文件復(fù)制到web-inf/ tags目錄中,而這一點(diǎn)很容易做到。剩下的事都交給jsp容器去做,它會(huì)把web-inf/tags目錄中找到的每個(gè)標(biāo)記文件轉(zhuǎn)換為標(biāo)記處理器。程序員完全擺脫了構(gòu)建標(biāo)記處理器的復(fù)雜工作。
下面舉個(gè)例子。這是標(biāo)記庫最簡單的形式,其中標(biāo)記文件只是簡單地把一個(gè)字符串寫到隱含對象中。
<%— example1.tag file, must reside in web-inf/tags —%><% out.println("hello from tag file.");%>
使用jsp頁面中的標(biāo)記庫再簡單不過了。和平常一樣,你只需taglib指令,通過前綴屬性在整個(gè)頁面中識(shí)別標(biāo)記庫。現(xiàn)在你有一個(gè)tagdir屬性,而不是uri屬性。tagdir屬性引用web-inf/tags目錄或web-inf/tags下的任何子目錄。
下面是一個(gè)使用example1.tag文件的jsp頁面的例子。
<%@ taglib prefix="easytag" tagdir="/web-inf/tags" %><easytag:example1></easytag:example1>
調(diào)用該jsp頁面瀏覽器上就會(huì)顯示下面的字符串:
hello from tag file.
結(jié)合上面講到的表達(dá)式語言,你就可以真正快速構(gòu)建無腳本的jsp頁面。再舉一個(gè)例子,下面的標(biāo)記文件(叫做example2.tag)通過調(diào)用jsp頁面接收一個(gè)屬性,并將它轉(zhuǎn)換為大寫字母。
<%— example2.tag file, must reside in web-inf/tags —%><%@ attribute name="x" %><% x = x.touppercase(); out.println(x);%>
下面是使用該標(biāo)記文件的jsp頁面:
<%@ taglib prefix="easytag" tagdir="/web-inf/tags" %><easytag:example2 x="hello"></easytag:example2>
下面是另一個(gè)例子,其中沒有java代碼:
<%— example3.tag file, must reside in web-inf/tags —%><%@ variable name-given="x" scope="at_begin" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c:set var="x" value="3"/>after: ${x}<jsp:dobody/>
該標(biāo)記文件用于下面的jsp頁面:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="easytag" tagdir="/web-inf/tags" %><c:set var="x" value="1"/>before: ${x}<br><easytag:example3/>請注意,要運(yùn)行本示例,在web-inf/lib目錄下要有jstl庫。
最后一個(gè)標(biāo)記文件示例還表明,不熟悉java編程語言的頁面創(chuàng)作者仍能利用標(biāo)記擴(kuò)展的強(qiáng)大功能。即便是java程序員,使用標(biāo)記文件也比編寫實(shí)施javax.servlet.jsp.tagext包中的某個(gè)接口的java類要方便。
結(jié)論
本文簡要闡述了servlet 2.4和jsp 2.0規(guī)范中的新特性,它們將包含在即將面世的j2ee 1.4中。servlet 2.4和jsp 2.0無疑將會(huì)加快web應(yīng)用程序的開發(fā)。
新聞熱點(diǎn)
疑難解答
圖片精選