javaGrowing

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            92 隨筆 :: 33 文章 :: 49 評(píng)論 :: 0 Trackbacks
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(12)

          隨筆分類(84)

          隨筆檔案(92)

          文章分類(32)

          文章檔案(33)

          相冊(cè)

          收藏夾(1)

          ajax

          java

          java專家論壇

          linux

          Oracle

          PHP

          sap

          xml

          其他

          好站鏈接

          英語學(xué)習(xí)

          軟件下載

          電子書

          搜索

          積分與排名

          最新隨筆

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          一、??????? Servlet 簡(jiǎn)介
          Servlet是對(duì)支持Java的服務(wù)器的一般擴(kuò)充。它最常見的用途是擴(kuò)展Web服務(wù)器,提供非常安全的、可移植的、易于使用的CGI替代品。它是一種動(dòng)態(tài)加載的模塊,為來自Web服務(wù)器的請(qǐng)求提供服務(wù)。它完全運(yùn)行在Java虛擬機(jī)上。由于它在服務(wù)器端運(yùn)行,因此它不依賴于瀏覽器的兼容性。
          servlet容器:
          負(fù)責(zé)處理客戶請(qǐng)求、把請(qǐng)求傳送給servlet并把結(jié)果返回給客戶。不同程序的容器實(shí)際實(shí)現(xiàn)可能有所變化,但容器與servlet之間的接口是由servlet?API定義好的,這個(gè)接口定義了servlet容器在servlet上要調(diào)用的方法及傳遞給servlet的對(duì)象類。
          servlet的生命周期:
          1、servlet容器創(chuàng)建servlet的一個(gè)實(shí)例
          2、容器調(diào)用該實(shí)例的init()方法
          3、如果容器對(duì)該servlet有請(qǐng)求,則調(diào)用此實(shí)例的service()方法
          4、容器在銷毀本實(shí)例前調(diào)用它的destroy()方法
          5、銷毀并標(biāo)記該實(shí)例以供作為垃圾收集
          一旦請(qǐng)求了一個(gè)servlet,就沒有辦法阻止容器執(zhí)行一個(gè)完整的生命周期。
          容器在servlet首次被調(diào)用時(shí)創(chuàng)建它的一個(gè)實(shí)例,并保持該實(shí)例在內(nèi)存中,讓它對(duì)所有的請(qǐng)求進(jìn)行處理。容器可以決定在任何時(shí)候把這個(gè)實(shí)例從內(nèi)存中移走。在典型的模型中,容器為每個(gè)servlet創(chuàng)建一個(gè)單獨(dú)的實(shí)例,容器并不會(huì)每接到一個(gè)請(qǐng)求就創(chuàng)建一個(gè)新線程,而是使用一個(gè)線程池來動(dòng)態(tài)的將線程分配給到來的請(qǐng)求,但是這從servlet的觀點(diǎn)來看,效果和為每個(gè)請(qǐng)求創(chuàng)建一個(gè)新線程的效果相同。
          servlet?API
          servlet接口:
          public?interface?Servlet
          它的生命周期由javax.servlet.servlet接口定義。當(dāng)你在寫servlet的時(shí)候必須直接或間接的實(shí)現(xiàn)這個(gè)接口。一般趨向于間接實(shí)現(xiàn):通過從javax.servlet.GenericServlet或javax.servlet.http.HttpServlet派生。在實(shí)現(xiàn)servlet接口時(shí)必須實(shí)現(xiàn)它的五個(gè)方法:
          init():
          public?void?init(ServletConfig?config)?throws?ServletException
          一旦對(duì)servlet實(shí)例化后,容器就調(diào)用此方法。容器把一個(gè)ServletConfig對(duì)象傳統(tǒng)給此方法,這樣servlet的實(shí)例就可以把與容器相關(guān)的配置數(shù)據(jù)保存起來供以后使用。如果此方法沒有正常結(jié)束就會(huì)拋出一個(gè)ServletException。一旦拋出該異常,servlet就不再執(zhí)行,而隨后對(duì)它的調(diào)用會(huì)導(dǎo)致容器對(duì)它重新載入并再次運(yùn)行此方法。接口規(guī)定對(duì)任何servlet實(shí)例,此方法只能被調(diào)用一次,在任何請(qǐng)求傳遞給servlet之前,此方法可以在不拋出異常的情況下運(yùn)行完畢。
          service():
          public?void?service(ServletRequest?req,ServletResponse?res)?throws?ServletException,IOException
          只有成功初始化后此方法才能被調(diào)用處理用戶請(qǐng)求。前一個(gè)參數(shù)提供訪問初始請(qǐng)求數(shù)據(jù)的方法和字段,后一個(gè)提供servlet構(gòu)造響應(yīng)的方法。
          destroy():
          public?void?destroy()
          容器可以在任何時(shí)候終止servlet服務(wù)。容器調(diào)用此方法前必須給service()線程足夠時(shí)間來結(jié)束執(zhí)行,因此接口規(guī)定當(dāng)service()正在執(zhí)行時(shí)destroy()不被執(zhí)行。
          getServletConfig():
          public?ServletConfig?getServletConfig()
          在servlet初始化時(shí),容器傳遞進(jìn)來一個(gè)ServletConfig對(duì)象并保存在servlet實(shí)例中,該對(duì)象允許訪問兩項(xiàng)內(nèi)容:初始化參數(shù)和ServletContext對(duì)象,前者通常由容器在文件中指定,允許在運(yùn)行時(shí)向sevrlet傳遞有關(guān)調(diào)度信息,后者為servlet提供有關(guān)容器的信息。此方法可以讓servlet在任何時(shí)候獲得該對(duì)象及配置信息。
          getServletInfo():
          public?String?getServletInfo()
          此方法返回一個(gè)String對(duì)象,該對(duì)象包含servlet的信息,例如開發(fā)者、創(chuàng)建日期、描述信息等。該方法也可用于容器。
          GenericServlet類
          Public?abstract?class?GenericServlet?implants?Servlet,ServletConfig,Serializable
          此類提供了servlet接口的基本實(shí)現(xiàn)部分,其service()方法被申明為abstract,因此需要被派生。init(ServletConfig?conf)方法把servletConfig對(duì)象存儲(chǔ)在一個(gè)private?transient(私有臨時(shí))實(shí)例變量里,getServletConfig()方法返回指向本對(duì)象的指針,如果你重載此方法,將不能使用getServletConfig來獲得ServletConfig對(duì)象,如果確實(shí)想重載,記住要包含對(duì)super.config的調(diào)用。2.1版的API提供一個(gè)重載的沒有參數(shù)的init()方法。現(xiàn)在在init(ServletConfig)方法結(jié)束時(shí)有一個(gè)對(duì)init()的調(diào)用,盡管目前它是空的。2.1版API里面,此類實(shí)現(xiàn)了ServletConfig接口,這使得開發(fā)者不用獲得ServletConfig對(duì)象情況下直接調(diào)用ServletConfig的方法,這些方法是:getInitParameter(),getInitParameterNames(),getServletContext。此類還包含兩個(gè)寫日志的方法,它們實(shí)際上調(diào)用的是ServletContext上的對(duì)應(yīng)方法。log(String?msg)方法將servlet的名稱和msg參數(shù)寫到容器的日志中,log(String?msg,Throwable?cause)除了包含servlet外還包含一個(gè)異常。
          HttpServlet類
          該類擴(kuò)展了GenericServlet類并對(duì)servlet接口提供了與HTTP更相關(guān)的實(shí)現(xiàn)。
          service():
          protected?void?service(HttpServletRequest?req,HttpServletResponse?res)?throws?ServletException,IOException
          public?void?service(HttpServletRequest?req,HttpServletResponse?res)throws?ServletException,IOException
          該方法作為HTTP請(qǐng)求的分發(fā)器,這個(gè)方法在任何時(shí)候都不能被重載。當(dāng)請(qǐng)求到來時(shí),service()方法決定請(qǐng)求的類型(GET,POST,HEAD,OPTIONS,DELETE,PUT,TRACE),并把請(qǐng)求分發(fā)給相應(yīng)的處理方法(doGet(),doPost(),doHead(),doOptions(),doDelete(),doPut(),doTrace())每個(gè)do方法具有和第一個(gè)service()相同的形式。為了響應(yīng)特定類型的HTTP請(qǐng)求,我們必須重載相應(yīng)的do方法。如果servlet收到一個(gè)HTTP請(qǐng)求而你沒有重載相應(yīng)的do方法,它就返回一個(gè)說明此方法對(duì)本資源不可用的標(biāo)準(zhǔn)HTTP錯(cuò)誤。
          getLatModified():
          protected?long?getLastModified(HttpServletRequest?req)
          該方法返回以毫秒為單位的的自GMT時(shí)間1970年1月1日0時(shí)0分0秒依賴的最近一次修改servlet的時(shí)間,缺省是返回一個(gè)負(fù)數(shù)表示時(shí)間未知。當(dāng)處理GET請(qǐng)求時(shí),調(diào)用此方法可以知道servlet的最近修改時(shí)間,服務(wù)器就可決定是否把結(jié)果從緩存中去掉。
          HttpServletRequest接口
          public?interface?HttpServletRequest?extends?ServletRequest
          所有實(shí)現(xiàn)此接口的對(duì)象(例如從servlet容器傳遞的HTTP請(qǐng)求對(duì)象)都能讓servlet通過自己的方法訪問所有請(qǐng)求的數(shù)據(jù)。下面是一些用來獲取表單數(shù)據(jù)的基本方法。
          getParameter()
          public?String?getParameter(String?key)
          此方法試圖將根據(jù)查詢串中的關(guān)鍵字定位對(duì)應(yīng)的參數(shù)并返回其值。如果有多個(gè)值則返回列表中的第一個(gè)值。如果請(qǐng)求信息中沒有指定參數(shù),則返回null。
          getParameterValues():
          public?String[]?getParameterValues(String?key)
          如果一個(gè)參數(shù)可以返回多個(gè)值,比如復(fù)選框集合,則可以用此方法獲得對(duì)應(yīng)參數(shù)的所有值。如果請(qǐng)求信息中沒有指定參數(shù),則返回null。
          GetParameterNames():
          Public?Enumeration?getParameterNames()
          此方法返回一個(gè)Enumeration對(duì)象,包含對(duì)應(yīng)請(qǐng)求的所有參數(shù)名字列表。
          HttpServletResponse接口
          public?interface?HttpServletResponse?extends?servletResponse
          servlet容器提供一個(gè)實(shí)現(xiàn)該接口的對(duì)象并通過service()方法將它傳遞給servlet。通過此對(duì)象及其方法,servlet可以修改響應(yīng)頭并返回結(jié)果。
          setContentType():
          public?void?setContentType(String?type)
          在給調(diào)用者發(fā)回響應(yīng)前,必須用此方法來設(shè)置HTTP響應(yīng)的MIME類型。可以是任何有效的MIME類型,當(dāng)給瀏覽器返回HTML是就是”text/html”類型。
          getWriter():
          public?PrintWriter?getWriter()throws?IOException
          此方法將返回PrintWriter對(duì)象,把servlet的結(jié)果作為文本返回給調(diào)用者。PrintWriter對(duì)象自動(dòng)把Java內(nèi)部的UniCode編碼字符轉(zhuǎn)換成正確的編碼以使客戶端能夠閱讀。
          getOutputStream():
          public?ServletOutputStream?getOutputStream()?throws?IOException
          此方法返回ServletOutputStream對(duì)象,它是java.io.OutputStream的一個(gè)子類。此對(duì)象向客戶發(fā)送二進(jìn)制數(shù)據(jù)。
          setHeader():
          public?void?setHeader(String?name,String?value)
          此方法用來設(shè)置送回給客戶的HTTP響應(yīng)頭。有一些快捷的方法用來改變某些常用的響應(yīng)頭,但有時(shí)也需要直接調(diào)用此方法。
          編譯條件
          需要從http://java.sun.com/products/servlet/?獲得一份JSDK的拷貝,并把servlet.jar移動(dòng)到JDK安裝目錄下的\jre\lib\ext目錄下。如果是JDK1.1,則移動(dòng)到\lib下,并在CLASSPATH中加入servlet.jar的絕對(duì)路徑。
          運(yùn)行條件
          需要Apache?Jserv,Jrun?Servlet?Exec,Java?Web?Server,Weblogic,WebSphere,Tomcat,Resin等servlet服務(wù)器端程序。
          簡(jiǎn)單范例

          import?java.io.*;
          import?javax.servlet.*;
          import?javax.servlet.http.*;

          public?class?HelloWorld?extends?HttpServlet?{

          ????public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)
          ????throws?IOException,?ServletException
          ????{
          ????????response.setContentType("text/html");
          ????????PrintWriter?out?=?response.getWriter();
          ????????out.println("<html>");
          ????????out.println("<body>");
          ????????out.println("<head>");
          ????????out.println("<title>Hello?World!</title>");
          ????????out.println("</head>");
          ????????out.println("<body>");
          ????????out.println("<h1>Hello?World!</h1>");
          ????????out.println("</body>");
          ????????out.println("</html>");
          ????}
          }

          servlet的性能和效率
          一個(gè)servlet僅被初始化一次而執(zhí)行多次,因此極小的低效性也會(huì)隨著時(shí)間的增加而產(chǎn)生很很大的影響。在代碼中需要考慮String對(duì)象的使用,如果產(chǎn)生HTML響應(yīng)需要用到很多字符串時(shí),不應(yīng)該為每一個(gè)字符串生成一個(gè)String對(duì)象,因?yàn)檫@會(huì)產(chǎn)生大量的String和StringBuffer對(duì)象,造成大量的對(duì)象構(gòu)造消耗和垃圾收集負(fù)擔(dān),解決的辦法是一行一行的把所有需要寫入的直接寫入PrintWriter中,或者創(chuàng)建一個(gè)StringBuffer對(duì)象,并使用append()方法將文本加入。
          及時(shí)回送
          有時(shí),程序需要花費(fèi)很長(zhǎng)時(shí)間執(zhí)行,在這種情況下應(yīng)該回送給客戶端一些信息,而不是長(zhǎng)時(shí)間顯示白屏,這可以在執(zhí)行到一定程度就回送一些東西,可以使用PrintWriter的flush()方法強(qiáng)制將現(xiàn)有的內(nèi)容回送給瀏覽器。

          Servlet會(huì)話
          由于Web服務(wù)器使用的協(xié)議HTTP是一種無狀態(tài)的協(xié)議,因此要維護(hù)單個(gè)客戶機(jī)一系列請(qǐng)求的當(dāng)前狀態(tài)就需要使用其它的附加手段,在以前,一般的方法是使用:
          l????隱藏的表格字段:在瀏覽器中,這種類型的字段是不可見的,然而,它在請(qǐng)求中被傳送,服務(wù)器端程序可以讀取它的值。它的優(yōu)點(diǎn)是實(shí)現(xiàn)容易,且大多瀏覽器都支持;缺點(diǎn)是字段必須按照特定的順序建立,客戶端可以通過查看源代碼得到其值,在瀏覽器中單擊“后退”按鈕會(huì)丟失加到當(dāng)前頁(yè)中的附加字段,同時(shí)也限制了用戶動(dòng)態(tài)的生成文檔。
          l????Cookie:是一些關(guān)鍵的數(shù)據(jù)片斷,由服務(wù)器建立并由客戶機(jī)的瀏覽器存放。瀏覽器維護(hù)一個(gè)它自己的Cookie表,這使得它可以作為一種會(huì)話跟蹤的解決方案。使用Cookie的好處是它比在URL或表單中儲(chǔ)存數(shù)據(jù)更直觀。它的缺點(diǎn)是它可以用于在比一次短會(huì)話更長(zhǎng)時(shí)間內(nèi)跟蹤用戶,甚至可以用來跟蹤某個(gè)用戶向站點(diǎn)發(fā)送的每一個(gè)請(qǐng)求,因此有人擔(dān)心自己的隱私問題而關(guān)閉了Cookie,一些老的瀏覽器也不支持cookie。Servlet?API提供一個(gè)Cookie類支持cookie,使用HttpServletResponse.addCookie()和HttpServletResponse.getCookies()方法添加和讀取cookie。
          l????URL重寫:修改請(qǐng)求的url,使之包含會(huì)話ID。這種方法的缺點(diǎn)是:對(duì)于大量的數(shù)據(jù),URL會(huì)變得很長(zhǎng)而失去控制;在某些環(huán)境下,URL的字符串長(zhǎng)度有一定的限制;數(shù)據(jù)保密問題,你可能不想讓旁邊的人或者可以使用同一個(gè)計(jì)算機(jī)的看到你的會(huì)話數(shù)據(jù)。Servlet提供HttpServletRequest類可以獲得參數(shù)。
          Servlet?API有自己內(nèi)置的會(huì)話跟蹤支持,使用HttpSession對(duì)象既可。它的setAttribute()方法綁定一對(duì)名字/值數(shù)據(jù),把它存到當(dāng)前會(huì)話中,如果會(huì)話中已經(jīng)存在該名字責(zé)替換它,語法為:public?void?setAttribute(String?name,Object?value)。getAttribute()方法讀取存儲(chǔ)在會(huì)話中的對(duì)象,語法為:public?Object?getAttribute(String?name)。getAttributeNames()方法返回存儲(chǔ)在會(huì)話中的所有名字,語法為:public?String[]?getAttributeNames()。最后一個(gè)方法是removeAttribute()方法,它從會(huì)話中刪除指定的信息,語法為:public?void?removeAttribute(String?name)。HttpSession對(duì)象可以使用HttpServletRequest對(duì)象request的getSession(true)方法獲得。參數(shù)為true意味著如果不存在該對(duì)象則創(chuàng)建一個(gè)。
          ?
          二、??? servlet 規(guī)范定義的 Servlet? 生命周期
          servlet有良好的生存期的定義,包括如何加載、實(shí)例化、初始化、處理客戶端請(qǐng)求以及如何被移除。這個(gè)生存期由javax.servlet.Servlet接口的init,service和destroy方法表達(dá)。
          1、加載和實(shí)例化
          容器負(fù)責(zé)加載和實(shí)例化一個(gè)servlet。實(shí)例化和加載可以發(fā)生在引擎啟動(dòng)的時(shí)候,也可以推遲到容器需要該servlet為客戶請(qǐng)求服務(wù)的時(shí)候。
          首先容器必須先定位servlet類,在必要的情況下,容器使用通常的Java類加載工具加載該servlet,可能是從本機(jī)文件系統(tǒng),也可以是從遠(yuǎn)程文件系統(tǒng)甚至其它的網(wǎng)絡(luò)服務(wù)。容器加載servlet類以后,它會(huì)實(shí)例化該類的一個(gè)實(shí)例。需要注意的是可能會(huì)實(shí)例化多個(gè)實(shí)例,例如一個(gè)servlet類因?yàn)橛胁煌某跏紖?shù)而有多個(gè)定義,或者servlet實(shí)現(xiàn)SingleThreadModel而導(dǎo)致容器為之生成一個(gè)實(shí)例池。

          2、初始化
          servlet加載并實(shí)例化后,容器必須在它能夠處理客戶端請(qǐng)求前初始化它。初始化的過程主要是讀取永久的配置信息,昂貴資源(例如JDBC連接)以及其它僅僅需要執(zhí)行一次的任務(wù)。通過調(diào)用它的init方法并給它傳遞唯一的一個(gè)(每個(gè)servlet定義一個(gè))ServletConfig對(duì)象完成這個(gè)過程。給它傳遞的這個(gè)配置對(duì)象允許servlet訪問容器的配置信息中的名稱-值對(duì)(name-value)初始化參數(shù)。這個(gè)配置對(duì)象同時(shí)給servlet提供了訪問實(shí)現(xiàn)了ServletContext接口的具體對(duì)象的方法,該對(duì)象描述了servlet的運(yùn)行環(huán)境。
          ????2.1初始化的錯(cuò)誤處理
          ????在初始化期間,servlet實(shí)例可能通過拋出UnavailableException?或者?ServletException異常表明它不能進(jìn)行有效服務(wù)。如果一個(gè)servlet拋出一個(gè)這樣的異常,它將不會(huì)被置入有效服務(wù)并且應(yīng)該被容器立即釋放。在此情況下destroy方法不會(huì)被調(diào)用因?yàn)槌跏蓟瘺]有成功完成。在失敗的實(shí)例被釋放后,容器可能在任何時(shí)候?qū)嵗粋€(gè)新的實(shí)例,對(duì)這個(gè)規(guī)則的唯一例外是如果失敗的servlet拋出的異常是UnavailableException并且該異常指出了最小的無效時(shí)間,那么容器就會(huì)至少等待該時(shí)間指明的時(shí)限才會(huì)重新試圖創(chuàng)建一個(gè)新的實(shí)例。
          ????2.2、工具因素
          ????當(dāng)工具(注:根據(jù)筆者的理解,這個(gè)工具可能是應(yīng)用服務(wù)器的某些檢查工具,通常是驗(yàn)證應(yīng)用的合法性和完整性)加載和內(nèi)省(introspect)一個(gè)web應(yīng)用時(shí),它可能加載和內(nèi)省該應(yīng)用中的類,這個(gè)行為將觸發(fā)那些類的靜態(tài)初始方法被執(zhí)行,因此,開發(fā)者不能假定只要當(dāng)servlet的init方法被調(diào)用后它才處于活動(dòng)容器運(yùn)行狀態(tài)(active?container?runtime)。作為一個(gè)例子,這意味著servlet不能在它的靜態(tài)(類)初始化方法被調(diào)用時(shí)試圖建立數(shù)據(jù)庫(kù)連接或者連接EJB容器。

          3、處理請(qǐng)求
          在servlet被適當(dāng)?shù)爻跏蓟螅萜骶涂梢允褂盟ヌ幚碚?qǐng)求了。每一個(gè)請(qǐng)求由ServletRequest類型的對(duì)象代表,而servlet使用ServletResponse回應(yīng)該請(qǐng)求。這些對(duì)象被作為service方法的參數(shù)傳遞給servlet。在HTTP請(qǐng)求的情況下,容器必須提供代表請(qǐng)求和回應(yīng)的HttpServletRequest和HttpServletResponse的具體實(shí)現(xiàn)。需要注意的是容器可能會(huì)創(chuàng)建一個(gè)servlet實(shí)例并將之放入等待服務(wù)的狀態(tài),但是這個(gè)實(shí)例在它的生存期中可能根本沒有處理過任何請(qǐng)求。
          ????3.1、多線程問題
          ????容器可能同時(shí)將多個(gè)客戶端的請(qǐng)求發(fā)送給一個(gè)實(shí)例的service方法,這也就意味著開發(fā)者必須確保編寫的servlet可以處理并發(fā)問題。如果開發(fā)者想防止這種缺省的行為,那么他可以讓他編寫的servlet實(shí)現(xiàn)SingleThreadModel。實(shí)現(xiàn)這個(gè)類可以保證一次只會(huì)有一個(gè)線程在執(zhí)行service方法并且一次性執(zhí)行完。容器可以通過將請(qǐng)求排隊(duì)或者維護(hù)一個(gè)servlet實(shí)例池滿足這一點(diǎn)。如果servlet是分布式應(yīng)用的一部分,那么,那么容器可能在該應(yīng)用分布的每個(gè)JVM中都維護(hù)一個(gè)實(shí)例池。如果開發(fā)者使用synchronized關(guān)鍵字定義service方法(或者是doGet和doPost),容器將排隊(duì)處理請(qǐng)求,這是由底層的java運(yùn)行時(shí)系統(tǒng)要求的。我們強(qiáng)烈推薦開發(fā)者不要同步service方法或者HTTPServlet的諸如doGet和doPost這樣的服務(wù)方法。
          ????3.2、處理請(qǐng)求中的異常
          ????servlet在對(duì)請(qǐng)求進(jìn)行服務(wù)的時(shí)候有可能拋出ServletException或者UnavailableException異常。ServletException表明在處理請(qǐng)求的過程中發(fā)生了錯(cuò)誤容器應(yīng)該使用合適的方法清除該請(qǐng)求。UnavailableException表明servlet不能對(duì)請(qǐng)求進(jìn)行處理,可能是暫時(shí)的,也可能是永久的。如果UnavailableException指明是永久性的,那么容器必須將servlet從服務(wù)中移除,調(diào)用它的destroy方法并釋放它的實(shí)例。如果指明是暫時(shí)的,那么容器可以選擇在異常信息里面指明的這個(gè)暫時(shí)無法服務(wù)的時(shí)間段里面不向它發(fā)送任何請(qǐng)求。在這個(gè)時(shí)間段里面被被拒絕的請(qǐng)求必須使用SERVICE_UNAVAILABLE?(503)返回狀態(tài)進(jìn)行響應(yīng)并且應(yīng)該攜帶稍后重試(Retry-After)的響應(yīng)頭表明不能服務(wù)只是暫時(shí)的。容器也可以選擇不對(duì)暫時(shí)性和永久性的不可用進(jìn)行區(qū)分而全部當(dāng)作永久性的并移除拋出異常的servlet。
          ????3.3線程安全
          ????開發(fā)者應(yīng)該注意容器實(shí)現(xiàn)的請(qǐng)求和響應(yīng)對(duì)象(注:即容器實(shí)現(xiàn)的HttpServletRequest和HttpServletResponese)沒有被保證是線程安全的,這就意味著他們只能在請(qǐng)求處理線程的范圍內(nèi)被使用,這些對(duì)象不能被其它執(zhí)行線程所引用,因?yàn)橐玫男袨槭遣淮_定的。

          4、服務(wù)結(jié)束
          容器沒有被要求將一個(gè)加載的servlet保存多長(zhǎng)時(shí)間,因此一個(gè)servlet實(shí)例可能只在容器中存活了幾毫秒,當(dāng)然也可能是其它更長(zhǎng)的任意時(shí)間(但是肯定會(huì)短于容器的生存期)
          當(dāng)容器決定將之移除時(shí)(原因可能是保存內(nèi)存資源或者自己被關(guān)閉),那么它必須允許servlet釋放它正在使用的任何資源并保存任何永久狀態(tài)(這個(gè)過程通過調(diào)用destroy方法達(dá)到)。容器在能夠調(diào)用destroy方法前,它必須允許那些正在service方法中執(zhí)行的線程執(zhí)行完或者在服務(wù)器定義的一段時(shí)間內(nèi)執(zhí)行(這個(gè)時(shí)間段在容器調(diào)用destroy之前)。一旦destroy方法被調(diào)用,容器就不會(huì)再向該實(shí)例發(fā)送任何請(qǐng)求。如果容器需要再使用該servlet,它必須創(chuàng)建新的實(shí)例。destroy方法完成后,容器必須釋放servlet實(shí)例以便它能夠被垃圾回收。
          ?
          三、??? serlvet 為什么只需要實(shí)現(xiàn) doGet doPost
          Serlvet接口只定義了一個(gè)服務(wù)方法就是service,而HttpServlet類實(shí)現(xiàn)了該方法并且要求調(diào)用下列的方法之一:
          doGet:處理GET請(qǐng)求
          doPost:處理POST請(qǐng)求
          doPut:處理PUT請(qǐng)求
          doDelete:處理DELETE請(qǐng)求
          doHead:處理HEAD請(qǐng)求
          doOptions:處理OPTIONS請(qǐng)求
          doTrace:處理TRACE請(qǐng)求
          通常情況下,在開發(fā)基于HTTP的servlet時(shí),開發(fā)者只需要關(guān)心doGet和doPost方法,其它的方法需要開發(fā)者非常的熟悉HTTP編程,因此這些方法被認(rèn)為是高級(jí)方法。
          而通常情況下,我們實(shí)現(xiàn)的servlet都是從HttpServlet擴(kuò)展而來。

          doPut和doDelete方法允許開發(fā)者支持HTTP/1.1的對(duì)應(yīng)特性;
          doHead是一個(gè)已經(jīng)實(shí)現(xiàn)的方法,它將執(zhí)行doGet但是僅僅向客戶端返回doGet應(yīng)該向客戶端返回的頭部的內(nèi)容;
          doOptions方法自動(dòng)的返回servlet所直接支持的HTTP方法信息;
          doTrace方法返回TRACE請(qǐng)求中的所有頭部信息。

          對(duì)于那些僅僅支持HTTP/1.0的容器而言,只有doGet,?doHead?和?doPost方法被使用,因?yàn)镠TTP/1.0協(xié)議沒有定義PUT,?DELETE,?OPTIONS,或者TRACE請(qǐng)求。

          另外,HttpServlet定義了getLastModified方法以支持有條件的(conditional)get操作。有條件的get操作是指使用GET方式請(qǐng)求資源并且在頭部指定只有在資源內(nèi)容在指定時(shí)間后被修改的情況下服務(wù)器才有必要回應(yīng)請(qǐng)求并發(fā)送請(qǐng)求的內(nèi)容。對(duì)于那些實(shí)現(xiàn)doGet方法并且在不同請(qǐng)求之間內(nèi)容相同的servlet而言,它應(yīng)該實(shí)現(xiàn)這個(gè)方法以提高網(wǎng)絡(luò)資源的利用率。

          另外要提及的是,按照規(guī)范的要求,servlet容器至少要實(shí)現(xiàn)HTTP/1.0協(xié)議規(guī)范,推薦實(shí)現(xiàn)HTTP/1.1規(guī)范,在此基礎(chǔ)上可以實(shí)現(xiàn)其它的基于請(qǐng)求回應(yīng)模式(based?request?response?model)的協(xié)議(例如HTTPS)。
          ?
          四、??? servlet 實(shí)例的個(gè)數(shù)及因此引發(fā)的問題
          在缺省情況下,一個(gè)容器中只為每個(gè)servlet定義生成一個(gè)servlet類實(shí)例。在servlet實(shí)現(xiàn)SingleThreadModel接口的情況下,容器可以生成多個(gè)實(shí)例以應(yīng)付沉重的請(qǐng)求,也可以將請(qǐng)求排隊(duì)發(fā)送給同一個(gè)實(shí)例(對(duì)于一個(gè)高性能的容器,也可能是這兩種方式的結(jié)合,因?yàn)閷?shí)例的個(gè)數(shù)是有限制的,因此在線程安全方式下一個(gè)實(shí)例會(huì)有多個(gè)請(qǐng)求排隊(duì)等待服務(wù)同時(shí)容器中多個(gè)實(shí)例可以對(duì)請(qǐng)求進(jìn)行服務(wù))。對(duì)于為可分布式(distributable)應(yīng)用開發(fā)的servlet而言,在每個(gè)JVM中對(duì)每個(gè)SERVLET定義都會(huì)有一個(gè)實(shí)例,如果在這樣的應(yīng)用中servlet也實(shí)現(xiàn)了SingleThreadModel接口,那么在每個(gè)JVM中每個(gè)servlet定義也可能有多個(gè)實(shí)例。

          使用SingleThreadModel接口可以保證一個(gè)線程一次性執(zhí)行完給定實(shí)例的service方法,需要注意的是這個(gè)保證只能應(yīng)用于servlet實(shí)例,那些可以被多個(gè)servlet實(shí)例訪問的對(duì)象(例如HttpSession實(shí)例)依然對(duì)多個(gè)servlet有效,即使他們實(shí)現(xiàn)了SingleThreadModel。

          根據(jù)規(guī)范中的這些說明,我們?cè)趯?shí)現(xiàn)自己的serlvet時(shí)需要考慮多線程的問題,一般而言,不要在servlet中定義可變的成員,只能定義一些常量(使用final定義,如果沒有使用,應(yīng)該注意在程序中不應(yīng)該修改其值),筆者見過一個(gè)定義很差的servlet:
          public?class?SomeHttpServlet?extends?HttpServlet?{

          ????HttpSession?session;
          ????...
          }

          這樣的servlet在使用中一定會(huì)出現(xiàn)問題,所有的用戶都會(huì)共用一個(gè)session(這樣很節(jié)約系統(tǒng)資源,不是嗎?:)),因此一個(gè)用戶請(qǐng)求的信息突然跑到另一個(gè)用戶的ie窗口豪不奇怪。
          而且,即使你的servlet實(shí)現(xiàn)了SingleThreadModel接口也不要定義可變的成員,因?yàn)樵摮蓡T的信息會(huì)保留下來,而這對(duì)于其它的用戶而言在絕大部分情況下是毫無意義的。(你確定會(huì)有意義的情況例外,例如某種計(jì)數(shù))

          另外需要說明的是上面說明中都是針對(duì)servlet定義而言的,而servlet定義定義不等價(jià)servlet類定義,即一個(gè)servlet類可能會(huì)有多個(gè)servlet定義,但是筆者還沒有找到“servlet定義”的定義,規(guī)范中提到實(shí)例化一個(gè)servlet時(shí)可能會(huì)有不同的初始參數(shù),但是這個(gè)也不同于帶參數(shù)的多個(gè)構(gòu)造方法。一般情況下我們可以認(rèn)為一個(gè)servlet類對(duì)應(yīng)一個(gè)servlet定義。
          ?
          五、??? servlet 會(huì)話
          HTTP協(xié)議是一種無狀態(tài)的協(xié)議,而對(duì)于現(xiàn)在的web應(yīng)用而言,我們往往需要記錄從特定客戶端的一系列請(qǐng)求間的聯(lián)系。現(xiàn)在已經(jīng)有很多會(huì)話跟蹤的技術(shù),但是對(duì)于程序員而言都不是很方便直接使用。servlet規(guī)范定義了一個(gè)簡(jiǎn)單的HttpSession接口以方便servlet容器進(jìn)行會(huì)話跟蹤而不需要開發(fā)者注意實(shí)現(xiàn)的細(xì)節(jié)。

          一般而言,有兩種最常用的會(huì)話跟蹤機(jī)制,一種就是URL重寫。在客戶端不接受cookie的情況下可以使用URL重寫進(jìn)行會(huì)話跟蹤。URL重寫包括向URL路徑添加一些容器可以解釋的數(shù)據(jù)。規(guī)范要求會(huì)話ID必須編碼在URL路徑中,參數(shù)名稱必須是jsessionid,例如:
          http://www.myserver.com/catalog/index.html;jsessionid=1234

          另一種就是現(xiàn)在最常用的cookie了,規(guī)范要求所有的servlet都必須支持cookie。容器向客戶端發(fā)送一個(gè)cookie,客戶端在后續(xù)的處于同一個(gè)會(huì)話的請(qǐng)求中向服務(wù)器返回該cookie。會(huì)話跟蹤cookie的名字必須是JSESSIONID。

          新出現(xiàn)的一種會(huì)話功能是SSL會(huì)話,SSL(Secure?Sockets?Layer,安全套接字層)是HTTPS協(xié)議使用的一種加密技術(shù),內(nèi)建了會(huì)話跟蹤功能,servlet容器可以非常容易的使用這些數(shù)據(jù)建立會(huì)話跟蹤。(但是HTTPS不是規(guī)范要求servlet必須支持的協(xié)議)?

          因?yàn)镠TTP是一種基于請(qǐng)求響應(yīng)的協(xié)議,因此會(huì)話只有在客戶端加入它以后才被新建立。當(dāng)會(huì)話跟蹤信息被成功的返回給服務(wù)器以指示會(huì)話給建立時(shí)客戶端才算加入了一個(gè)會(huì)話。如果客戶端沒有加入會(huì)話,那么下一次請(qǐng)求不會(huì)被認(rèn)為是會(huì)話的一部分。如何客戶端還不知道會(huì)話或者客戶端選擇不加入一個(gè)會(huì)話,那么會(huì)話被認(rèn)為是新的。開發(fā)者必須自己設(shè)計(jì)自己的應(yīng)用中的會(huì)話處理狀態(tài),在什么地方?jīng)]有加入會(huì)話,什么地方不能加入會(huì)話以及什么地方不需要加入會(huì)話。
          規(guī)范要求HttpSession在應(yīng)用或者servlet上下文級(jí)別有效,諸如cookie這樣的建立會(huì)話的底層機(jī)制可以在上下文中共享,但是對(duì)于那些外露的對(duì)象,以及更重要的是對(duì)象的那些屬性是不能在上下文中共享的。

          對(duì)于會(huì)話的屬性的綁定而言,任何對(duì)象都可以綁定到某個(gè)命名屬性。被綁定的屬性對(duì)象對(duì)于其它處于相同ServletContext并且處于同一個(gè)會(huì)話處理中的其它servlet也是可見的。
          某些對(duì)象在被加入會(huì)話或者被從會(huì)話中移除時(shí)要求得到通知,這樣的信息可以通過讓該對(duì)象實(shí)現(xiàn)HttpSessionBindingListener接口得到。該接口定義了兩個(gè)方法用以標(biāo)記被綁定到會(huì)話或者從會(huì)話中被移除。
          valueBound方法在對(duì)象通過getAttribute之前就被調(diào)用,而valueUnbound方法在對(duì)象已經(jīng)不能通過getAttribute得到后才被調(diào)用。

          由于HTTP是無狀態(tài)協(xié)議,因此客戶端不再活動(dòng)時(shí)沒有什么明顯的信號(hào),這也就意味著只有一種機(jī)制可以用于表明客戶端不再活動(dòng):超時(shí)。會(huì)話的缺省的時(shí)限由servlet容器定義并且可以通過HttpSession的getMaxInactiveInterval得到,開發(fā)者也可以通過使用setMaxInactiveInterval方法進(jìn)行設(shè)置,這些方法返回的單位是秒,如果時(shí)限被設(shè)置為-1,那么意味著永遠(yuǎn)不會(huì)超時(shí)。

          通過調(diào)用HttpSession的getLastAccessedTime方法,我們可以得到在當(dāng)前請(qǐng)求之前的訪問時(shí)間。當(dāng)會(huì)話中的一個(gè)請(qǐng)求被servlet上下文處理時(shí)會(huì)話就被認(rèn)為被訪問了。

          另外需要注意的就是一些很重要的會(huì)話的語義問題。
          多線程問題:多個(gè)請(qǐng)求線程可能會(huì)同時(shí)訪問同一個(gè)會(huì)話,開發(fā)者有責(zé)任以適當(dāng)?shù)姆绞酵皆L問會(huì)話中的資源。
          分布式環(huán)境:對(duì)于被標(biāo)記為可分布的應(yīng)用而言,同一會(huì)話中的所有請(qǐng)求只能被單一的VM處理。同時(shí),放入HttpSession中的所有對(duì)象都必須實(shí)現(xiàn)Serializable接口,否則容器可能會(huì)拋出IllegalArgumentException(在jboss_tomcat下沒有拋出這個(gè)異常,但是如果在關(guān)閉服務(wù)器時(shí)還有未完成的會(huì)話,那么服務(wù)器在試圖存儲(chǔ)會(huì)話時(shí)會(huì)出現(xiàn)串行化異常,在重新啟動(dòng)的時(shí)候會(huì)試圖回復(fù)會(huì)話,也會(huì)出現(xiàn)異常)。這個(gè)限制意味著開發(fā)者不會(huì)遇到非可分布容器中的那些并發(fā)問題。另外容器提供者可以通過將一個(gè)會(huì)話對(duì)象以及它的內(nèi)容從分布式系統(tǒng)的一個(gè)活動(dòng)節(jié)點(diǎn)移動(dòng)到系統(tǒng)的其它不同節(jié)點(diǎn)的能力來保證可伸縮性。
          客戶端的語義:基于cookie或者SSL證書通常是被web瀏覽器控制并且不聯(lián)系到特定瀏覽器窗口的事實(shí),從客戶端應(yīng)用的所有窗口發(fā)送到容器的請(qǐng)求都可能是同一個(gè)會(huì)話。為了達(dá)到最大的可移植性,開發(fā)者不能總假設(shè)特定客戶端的所有窗口的請(qǐng)求都處于同一個(gè)會(huì)話中。
          六、??? Bean Servlet 的企業(yè)應(yīng)用
          J2EE是一個(gè)企業(yè)應(yīng)用程序的開發(fā)平臺(tái),包括了對(duì)EJB、Servlet、JavaServer?Page、JNDI、XML等的支持。在這個(gè)平臺(tái)上可以開發(fā)瘦客戶端的多層體系結(jié)構(gòu)的企業(yè)應(yīng)用程序。

            Enterprise?JavaBean技術(shù)是J2EE的主要基礎(chǔ)。EJB技術(shù)對(duì)在分布式的計(jì)算環(huán)境中執(zhí)行應(yīng)用邏輯提供了一個(gè)可伸縮的框架結(jié)構(gòu)。J2EE通過將EJB組件結(jié)構(gòu)和其它的企業(yè)技術(shù)相結(jié)合,解決了在Java平臺(tái)上進(jìn)行開發(fā)和配置服務(wù)端應(yīng)用程序的無縫結(jié)合。

            要使用J2EE開發(fā)您的企業(yè)應(yīng)用,您必須要在您的機(jī)器上安裝一個(gè)Web服務(wù)器,還要支持XML。為了在瀏覽器中運(yùn)行Java?2的API,還要給您的瀏覽器安裝一個(gè)支持Java2的插件。

            下面就介紹怎樣用J2EE?SDK寫一個(gè)包括了HTML頁(yè)面,Servlet和Session?Bean的一個(gè)簡(jiǎn)單的瘦客戶端的多層體系結(jié)構(gòu)的企業(yè)應(yīng)用程序。聽起來是不是心動(dòng)了呢?下面就開始吧。

          還要提醒一點(diǎn)的就是:在編程的時(shí)候,適當(dāng)?shù)脑黾觕atch子句是一個(gè)很好編程風(fēng)格。如果例子代碼拋出了一個(gè)正確的異常,代碼就被?try/catch這樣的程序結(jié)構(gòu)包圍。Catch子句應(yīng)該中應(yīng)該加入處理異常的代碼,千萬不要留成空白。至少,應(yīng)該加入語句:e.printStackTrace()來在控制臺(tái)顯示異常信息。

            J2EE?SDK是一個(gè)J2EE平臺(tái)上用于演示、教育等用途的非商業(yè)的東東。可以從javasoft的網(wǎng)站上免費(fèi)下載。很適合用來學(xué)習(xí)。如果你沒有出國(guó)權(quán)限,還可以從國(guó)內(nèi)各高校的FTP服務(wù)器上去下載,速度比較快,但可能版本不是最新的。


          瘦客戶端的多層體系結(jié)構(gòu)的應(yīng)用程序的例子:

            本例子通過一個(gè)HTML頁(yè)面的輸入來調(diào)用一個(gè)Servlet,Servlet再用Java的名字目錄服務(wù)接口(JNDI)APIs來尋找一個(gè)會(huì)話Session?Bean,用這個(gè)Session?Bean來執(zhí)行一個(gè)計(jì)算。當(dāng)Servlet得到了計(jì)算的結(jié)果的之后,Servlet把計(jì)算結(jié)果返回給HTML頁(yè)面的用戶。

            之所以說這是一個(gè)瘦客戶端的應(yīng)用程序,是因?yàn)镾ervlet本身并沒有執(zhí)行任何的應(yīng)用邏輯。這個(gè)簡(jiǎn)單的計(jì)算是由一個(gè)Session?Bean在J2EE的應(yīng)用服務(wù)器上執(zhí)行的。客戶沒有參與過程的任何操作,所有的計(jì)算都是由Session?Bean完成的。

            所謂的多層體系結(jié)果實(shí)際上是由三或者四層組成的。我們的例子實(shí)際上是四層的一個(gè)結(jié)構(gòu)。三層的體系結(jié)構(gòu)是在標(biāo)準(zhǔn)的兩層的客戶/服務(wù)器結(jié)構(gòu)基礎(chǔ)上,將一個(gè)多線程的應(yīng)用服務(wù)器加到了非瀏覽器的客戶端和后臺(tái)數(shù)據(jù)庫(kù)之間。而四層的體系結(jié)構(gòu)是通過Servlet和JavaServer?Pages技術(shù)將客戶端的應(yīng)用程序由瀏覽器和HTML頁(yè)面來取代。這個(gè)例子我們暫時(shí)只用其中的三層,在下一個(gè)例子中。我們?cè)偃ピL問數(shù)據(jù)庫(kù)。這樣,就擴(kuò)展到四層了。再以后,我們會(huì)涉及到JavaServer?Pages技術(shù)和XML技術(shù)。


          J2EE軟件的安裝:

            為了使我們的例子能夠運(yùn)行起來,首先要下載一個(gè)Java2?SDK?Enterprise?Edition(J2EE)的1.2.1的版本和一個(gè)J2SE(Java?2?Standard?Edition)的1.2以上的版本。在Windows?2000系統(tǒng)中,假設(shè)我們把J2EE和J2SE都裝到了C:\J2EE目錄下。安裝詳細(xì)目錄如下:

          J2EE:C:\J2EE\j2sdkee1.2.1

          J2SE:C:\J2EE\jdk1.2.2


          Path和ClassPath的設(shè)置:

            下載的東西包括了J2EE的應(yīng)用服務(wù)器、Cloudscape數(shù)據(jù)庫(kù)、使用了加密套接字協(xié)議層的Web服務(wù)器、開發(fā)和配置的工具、企業(yè)級(jí)的Java?APIs。其Path和ClassPath的設(shè)置如下:

          Path的設(shè)置:在Windows系統(tǒng)中,需要把Path的目錄包含下面兩個(gè)目錄:

          C:\J2EE\j2sdkee1.2.1\bin

          C:\J2EE\jdk1.2.2\bin

          Classpath的設(shè)置:在Windows系統(tǒng)中,需要把Classpath參數(shù)包含下面的文件:

          C:\J2EE\j2sdkee.1.2.1\lib\j2ee.jar

          另外,還要配置環(huán)境變量:

          J2EE_HOME=C:\J2EE\j2sdkee1.2.1

          JAVA_HOME=C:\J2EE\jdk1.2.2

            這樣,就可以執(zhí)行C:\J2EE\j2sdkee1.2.1\bin目錄下面的批處理命令了。仔細(xì)看看里面的批處理,你會(huì)發(fā)現(xiàn)不少的東西的。


          J2EE應(yīng)用程序組件:

            J2EE程序員編寫J2EE組件。J2EE組件是一個(gè)功能齊全的軟件單元。將其它的應(yīng)用程序組件組裝到J2EE的應(yīng)用程序和接口中。J2EE規(guī)范中定義如下的應(yīng)用程序組件:


          應(yīng)用程序客戶組件


          Enterprise?JavaBean組件


          Servlet和JavaServer?Pages組件(也叫做Web組件)


          Applet

            在本例子中,我們創(chuàng)建了一個(gè)J2EE的應(yīng)用程序和兩個(gè)J2EE的組件:一個(gè)Servlet和一個(gè)Session?Bean。Servlet和HTML文件是捆綁在一個(gè)WAR(WEB?Archive)文件中。Session?Bean的類和接口捆綁到了一個(gè)JAR文件中。然后再把WAR文件和JAR文件加到J2EE的應(yīng)用程序,捆綁到一個(gè)EAR(Enterprise?Archive)文件中。并驗(yàn)證測(cè)試產(chǎn)品環(huán)境的配置。

            在這所有的步驟中。實(shí)際上執(zhí)行了很多的不用的角色的功能。編寫Session?Bean和Servlet是開發(fā)工作。而創(chuàng)建一個(gè)J2EE的應(yīng)用程序,將J2EE組件組裝到應(yīng)用程序中是應(yīng)用程序的組裝工作。實(shí)際上,這些工作可以在不同的地方由不用的人員來做。

          創(chuàng)建一個(gè)HTML頁(yè)面:

          這個(gè)頁(yè)面名字為bonus.html。HTML代碼如下:

            代碼中,讓人感興趣的是用別名來調(diào)用BonusServlet.class。因?yàn)樵诤竺嫣岬降膽?yīng)用程序的組裝的時(shí)候,將它映射到了這個(gè)別名BonusServlet上

          <HTML>

          <BODY?BGCOLOR?=?"WHITE">

          <BLOCKQUOTE>

          <H3>Bonus?Calculation</H3>

          <FORM?METHOD="GET"?ACTION="BonusAlias">

          <P>

          Enter?social?security?Number:

          <P>

          <INPUT?TYPE="TEXT"?NAME="SOCSEC"></INPUT>

          <P>

          Enter?Multiplier:

          <P>

          <INPUT?TYPE="TEXT"?NAME="MULTIPLIER"></INPUT>

          <P>

          <INPUT?TYPE="SUBMIT"?VALUE="Submit">

          <INPUT?TYPE="RESET">

          </FORM>

          </BLOCKQUOTE>

          </BODY>

          </HTML>

            這個(gè)HTML文件有兩個(gè)數(shù)據(jù)域,用戶可以輸入社會(huì)保險(xiǎn)號(hào)和一個(gè)乘數(shù)。當(dāng)用戶單擊了Submit按紐。BonusServlet就得到了終端用戶的數(shù)據(jù)。然后尋找Session?Bean。將用戶數(shù)據(jù)傳遞給Session?Bean。Session?Bean計(jì)算出獎(jiǎng)金,把結(jié)果返回給Servlet。Servlet再通過另一個(gè)HTML頁(yè)面將獎(jiǎng)金結(jié)果返回給用戶。



          創(chuàng)建Servlet:

          例子假定BonusServlet.java文件是在C:\J2EE\Client-Code目錄下面。在運(yùn)行的時(shí)候,Servlet代碼執(zhí)行如下操作:


          獲得用戶數(shù)據(jù)


          查找Session?Bean


          將用戶數(shù)據(jù)傳遞給Session?Bean


          在得到Session?Bean的返回結(jié)果以后,創(chuàng)建一個(gè)HTML頁(yè)面將結(jié)果返回給客戶。


          Servlet代碼如下:

          import?javax.servlet.*;

          import?javax.servlet.http.*;

          import?java.io.*;

          import?javax.naming.*;

          import?javax.rmi.PortableRemoteObject;

          import?Beans.*;

          public?class?BonusServlet?extends?HttpServlet?{

          CalcHome?homecalc;

          public?void?init(ServletConfig?config)

          throws?ServletException{

          //Look?up?home?interface

          try{

          InitialContext?ctx?=?new?InitialContext();

          Object?objref?=?ctx.lookup("calcs");

          homecalc?=

          (CalcHome)PortableRemoteObject.narrow(

          objref,

          CalcHome.class);

          }?catch?(Exception?NamingException)?{

          NamingException.printStackTrace();

          }?

          }

          public?void?doGet?(HttpServletRequest?request,

          HttpServletResponse?response)

          throws?ServletException,?IOException?{

          String?socsec?=?null;

          int?multiplier?=?0;

          double?calc?=?0.0;

          PrintWriter?out;

          response.setContentType("text/html");

          String?title?=?"EJB?Example";

          out?=?response.getWriter();

          out.println("<HTML><HEAD><TITLE>");

          out.println(title);

          out.println("</TITLE></HEAD><BODY>");

          try{

          Calc?theCalculation;

          //Get?Multiplier?and?Social?Security?Information

          String?strMult?=

          request.getParameter("MULTIPLIER");

          Integer?integerMult?=?new?Integer(strMult);

          multiplier?=?integerMult.intValue();

          socsec?=?request.getParameter("SOCSEC");

          //Calculate?bonus.10?AUGUST?28,?2000

          double?bonus?=?100.00;

          theCalculation?=?homecalc.create();

          calc?=

          theCalculation.calcBonus(multiplier,?bonus);

          }?catch(Exception?CreateException){

          CreateException.printStackTrace();

          }

          //Display?Data

          out.println("<H1>Bonus?Calculation</H1>");

          out.println("<P>Soc?Sec:?"?+?socsec?+?"<P>");

          out.println("<P>Multiplier:?"?+

          multiplier?+?"<P>");

          out.println("<P>Bonus?Amount:?"?+?calc?+?"<P>");

          out.println("</BODY></HTML>");

          out.close();

          }

          public?void?destroy()?{

          System.out.println("Destroy");

          }

          }


            在import子句中,javax.servlet包括了Servlet?Class的協(xié)議。Java.io是系統(tǒng)輸入輸出包。Javax.naming里面包含了Java名字目錄服務(wù)APIs。Javax.rmi是用來Session?Bean的home接口和Remote對(duì)象的通信使用的。

            在BonusServlet.init方法中,查找Session?Bean的home接口。并且產(chǎn)生它的實(shí)例。方法使用了JNDI在組件的組裝中的指定的名字calcs。用它來得到home接口的reference。然后就把這個(gè)reference和home接口類傳遞給PortableRemoteObject.narrow方法。來保證把reference轉(zhuǎn)化為CalcHome類型。

            DoGet()方法有兩個(gè)參數(shù)。一個(gè)是request對(duì)象,另一個(gè)是reponse對(duì)象。瀏覽器發(fā)送一個(gè)request對(duì)象給Servlet。而Servlet返回一個(gè)response對(duì)象給瀏覽器。方法訪問request對(duì)象里面的信息,可以發(fā)現(xiàn)是誰在發(fā)出的請(qǐng)求、請(qǐng)求的數(shù)據(jù)在什么表單里面、是哪個(gè)HTTP頭被發(fā)送。并使用reponse對(duì)象產(chǎn)生一個(gè)HTML頁(yè)面來響應(yīng)瀏覽器的請(qǐng)求。

            當(dāng)方法處理請(qǐng)求的時(shí)候,如果產(chǎn)生輸入輸出錯(cuò)誤,就拋出一個(gè)IOException異常。如果不能處理請(qǐng)求,就會(huì)拋出一個(gè)ServletException異常。為了計(jì)算獎(jiǎng)金值,doGet()創(chuàng)建了一個(gè)home接口,調(diào)用它的calcBonus。


          創(chuàng)建Session?Bean:

            Session?Bean代表了與客戶的一個(gè)短暫的會(huì)話。如果服務(wù)或者客戶有一方崩潰了。數(shù)據(jù)就消失了。相反,Entity?Bean代表了數(shù)據(jù)庫(kù)中一段持久的數(shù)據(jù)。如果服務(wù)或者客戶又一方崩潰了,底層的服務(wù)保證數(shù)據(jù)能被保存下來。

            因?yàn)檫@個(gè)Enterprise?Bean只是應(yīng)BonusServlet的請(qǐng)求,執(zhí)行了一個(gè)簡(jiǎn)單的計(jì)算。如果發(fā)生崩潰,可以重新初始化計(jì)算。這樣,我們?cè)诒纠又芯瓦x擇Session?Bean來實(shí)現(xiàn)這個(gè)計(jì)算。

           在組裝配置好以后,Servlet組件和Session?Bean組件如何在一個(gè)J2EE應(yīng)用程序中協(xié)同工作。容器是Session?Bean和支持Session?Bean的底層平臺(tái)之間的接口。容器是在配置期間產(chǎn)生的。

            本例子假定CalcBean.java、Calc.java和CalcHome.java文件都放在C:\J2EE\Beans目錄下面。CalcHome.java文件前面的Package名字?Beans和目錄Beans的名字應(yīng)該是一樣的。當(dāng)這些文件被編譯的時(shí)候,是從Beans目錄中編譯,其名字是包的名字后面加一個(gè)斜線在加上類或者接口的名字。


           

          CalcHome.java文件:

          package?Beans;

          import?java.rmi.RemoteException;

          import?javax.ejb.CreateException;

          import?javax.ejb.EJBHome;

          public?interface?CalcHome?extends?EJBHome?{

          Calc?create()?throws?CreateException,?RemoteException;

          }

            BonusServlet并不直接同Session?Bean通信。而是通過產(chǎn)生一個(gè)CalcHome的實(shí)例。這個(gè)Home接口擴(kuò)展了EJBHome接口。有一個(gè)Create()方法,用來在容器中產(chǎn)生一個(gè)Session?Bean。如果無法產(chǎn)生Session?Bean,將會(huì)拋出一個(gè)CreateException異常。如果不能與Session?Bean的遠(yuǎn)程方法通信,就會(huì)拋出一個(gè)RemoteException異常。


          Calc.java文件:

          package?Beans;

          import?javax.ejb.EJBObject;

          import?java.rmi.RemoteException;

          public?interface?Calc?extends?EJBObject?{

          public?double?calcBonus(int?multiplier,

          double?bonus)

          throws?RemoteException;

          }

            產(chǎn)生一個(gè)Home接口以后,J2EE應(yīng)用程序就創(chuàng)建一個(gè)Remote接口和一個(gè)Session?Bean。Remote接口擴(kuò)展了EJBObject接口。并且聲明了一個(gè)calcBonus()方法來計(jì)算獎(jiǎng)金值。方法需要拋出javax.rmi.RemoteException異常。方法的實(shí)現(xiàn)在CalcBean類里面。


          CalcBean.java文件:

          package?Beans;

          import?java.rmi.RemoteException;

          import?javax.ejb.SessionBean;

          import?javax.ejb.SessionContext;

          public?class?CalcBean?implements?SessionBean?{?

          public?double?calcBonus(int?multiplier,

          double?bonus)?{

          double?calc?=?(multiplier*bonus);

          return?calc;

          }

          public?void?ejbCreate()?{?}

          public?void?setSessionContext(

          SessionContext?ctx)?{?}

          public?void?ejbRemove()?{?}

          public?void?ejbActivate()?{?}

          public?void?ejbPassivate()?{?}

          public?void?ejbLoad()?{?}

          public?void?ejbStore()?{?}

          }

            本Session?Bean類實(shí)現(xiàn)了SessionBean接口,提供了CalcBonus()方法的行為。在BonusServlet調(diào)用CalcHome的Create()方法以后,依次調(diào)用setSessionContext()方法和ejbCreate()方法。

            這些空的方法是從SessionBean中來的。由容器負(fù)責(zé)調(diào)用。除非在Bean的創(chuàng)建或者刪除里面,你需要附加一些你自己的操作。否者,你并不需要提供這些方法的行為。
          七、
          posted on 2007-06-20 10:27 javaGrowing 閱讀(853) 評(píng)論(0)  編輯  收藏 所屬分類: java學(xué)習(xí)
          主站蜘蛛池模板: 白山市| 乐清市| 内乡县| 磐安县| 个旧市| 永修县| 镶黄旗| 合水县| 江山市| 绥阳县| 凤城市| 南充市| 博野县| 曲阳县| 连城县| 商水县| 孝感市| 德令哈市| 石景山区| 吉林市| 中宁县| 天柱县| 海南省| 巩留县| 来凤县| 乾安县| 麻栗坡县| 仪陇县| 襄汾县| 武功县| 桂东县| 杭锦旗| 临清市| 榆社县| 阿坝县| 大同县| 溧水县| 黄山市| 洛隆县| 社会| 台湾省|