<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    javaGrowing

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      92 隨筆 :: 33 文章 :: 49 評(píng)論 :: 0 Trackbacks
    一、??????? 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)前頁中的附加字段,同時(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ù)庫連接或者連接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頁面,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í)。如果你沒有出國權(quán)限,還可以從國內(nèi)各高校的FTP服務(wù)器上去下載,速度比較快,但可能版本不是最新的。


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

      本例子通過一個(gè)HTML頁面的輸入來調(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頁面的用戶。

      之所以說這是一個(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ù)庫之間。而四層的體系結(jié)構(gòu)是通過Servlet和JavaServer?Pages技術(shù)將客戶端的應(yīng)用程序由瀏覽器和HTML頁面來取代。這個(gè)例子我們暫時(shí)只用其中的三層,在下一個(gè)例子中。我們?cè)偃ピL問數(shù)據(jù)庫。這樣,就擴(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ù)庫、使用了加密套接字協(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頁面:

    這個(gè)頁面名字為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頁面將獎(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頁面將結(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ī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ù)庫中一段持久的數(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 閱讀(849) 評(píng)論(0)  編輯  收藏 所屬分類: java學(xué)習(xí)
    主站蜘蛛池模板: 99re在线精品视频免费| 久久夜色精品国产噜噜亚洲a| 国产自偷亚洲精品页65页| 国产免费人成视频在线观看| 成人片黄网站A毛片免费| 99在线视频免费观看视频| 波多野结衣免费在线| 99在线视频免费观看视频| 日韩精品无码区免费专区| 一二三四在线播放免费观看中文版视频 | 1000部拍拍拍18免费网站| 97久久免费视频| 亚洲视频免费播放| 亚洲一区二区三区免费视频| 18禁止观看免费私人影院| 黄页网站在线观看免费高清| 久久久久久久久免费看无码| 在线免费观看一级片| 国产无遮挡吃胸膜奶免费看视频| 国产aa免费视频| 亚洲综合色成在线播放| 亚洲国产AV无码专区亚洲AV| 亚洲精品91在线| 亚洲性线免费观看视频成熟| 亚洲国产精品久久久久秋霞小| 特级毛片在线大全免费播放| 国产无遮挡又黄又爽免费网站| 日韩精品在线免费观看| 四虎在线最新永久免费| 毛片免费观看网站| 免费国产美女爽到喷出水来视频| 亚洲成片观看四虎永久| 国产A在亚洲线播放| 亚洲国产精品成人综合久久久| 亚洲国产精品无码中文lv| 日韩在线观看免费| 国产精品白浆在线观看免费| 丁香花免费高清视频完整版| 免费大黄网站在线观看| 亚洲精品成人片在线观看精品字幕| 亚洲麻豆精品果冻传媒|