補發(fā)之前遺漏的一章,pdf版的沒問題,下載地址: http://jinnianshilongnian.iteye.com/admin/blogs/1775987
?
Servlet接口是Java Servlet API的核心抽象。所有Servlet類必須直接或間接的實現(xiàn)該接口,或者更通常做法是通過繼承一個實現(xiàn)了該接口的類從而復(fù)用許多共性功能。目前有GenericServlet和HttpServlet這兩個類實現(xiàn)了Servlet接口。大多數(shù)情況下,開發(fā)者只需要繼承HttpServlet去實現(xiàn)自己的Servlet即可。
?
2.1 請求處理方法
Servlet基礎(chǔ)接口定義了用于客戶端請求處理的service方法。當(dāng)有請求到達(dá)時,該方法由servlet容器路由到一個servlet實例。
Web應(yīng)用程序的并發(fā)請求處理通常需要Web開發(fā)人員去設(shè)計適合多線程執(zhí)行的Servlet,從而保證service方法能在一個特定時間點處理多線程并發(fā)執(zhí)行。(注:即Servlet默認(rèn)是線程不安全的,需要開發(fā)人員處理多線程問題)
通常Web容器對于并發(fā)請求將使用同一個servlet處理,并且在不同的線程中并發(fā)執(zhí)行service方法。
?
2.1.1 基于Http規(guī)范的請求處理方法
HttpServlet抽象子類在Servlet接口基礎(chǔ)之上添加了些協(xié)議相關(guān)的方法,并且這些方法能根據(jù)HTTP請求類型自動的由HttpServlet中實現(xiàn)的service方法轉(zhuǎn)發(fā)到相應(yīng)的協(xié)議相關(guān)的處理方法上。這些方法是:
■ doGet處理HTTP GET請求
■ doPost處理HTTP POST請求
■ doPut處理HTTP PUT請求
■ doDelete處理HTTP DELETE請求
■ doHead處理HTTP HEAD請求
■ doOptions處理HTTP OPTIONS請求
■ doTrace處理HTTP TRACE請求
一般情況下,當(dāng)開發(fā)基于HTTP協(xié)議的Servlet時,Servlet開發(fā)人員將僅去實現(xiàn)doGet 和 doPost請求處理方法即可。如果開發(fā)人員想使用其他處理方法,其使用方式跟之前的是類似的,即HTTP編程都是類似。
?
2.1.2 附加的方法
doPut和doDelete方法允許Servlet開發(fā)人員讓支持HTTP/1.1的客戶端使用這些功能。HttpServlet中的doHead方法可以認(rèn)為是doGet方法的一個特殊形式,它僅返回由doGet方法產(chǎn)生的header信息。doOptions方法返回當(dāng)前servlet支持的HTTP方法(譯者注:通過Allow響應(yīng)頭返回支持的HTTP操作,如GET、POST)。doTrace方法返回的響應(yīng)包含TRACE請求的所有頭信息。
?
2.1.3 有條件GET支持
HttpServlet定義了用于支持有條件GET操作的getLastModified方法。所謂的有條件GET操作是指客戶端通過GET請求獲取資源時,當(dāng)資源自第一次獲取那個時間點發(fā)生更改后才再次發(fā)生數(shù)據(jù),否則將使用客戶端緩存的數(shù)據(jù)。在一些適當(dāng)?shù)膱龊希瑢崿F(xiàn)此方法可以更有效的利用網(wǎng)絡(luò)資源,減少不必要的數(shù)據(jù)發(fā)送。
?
2.2 實例數(shù)量
通過注解描述的(第8章 注解和可插拔性)或者在Web應(yīng)用程序的部署描述符(第14章 部署描述符)中描述的servlet聲明,控制著servlet容器如何提供servlet實例。
對于未托管在分布式環(huán)境中(默認(rèn))的servlet而言,servlet容器對于每一個Servlet聲明必須且只能產(chǎn)生一個實例。不過,如果Servlet實現(xiàn)了SingleThreadModel接口,servlet容器可以選擇實例化多個實例以便處理高負(fù)荷請求或者串行化請求到一個特定實例。
如果servlet以分布式方式進(jìn)行部署,容器可以為每個虛擬機(JVM)的每個Servlet聲明產(chǎn)生一個實例。但是,如果在分布式環(huán)境中servlet實現(xiàn)了SingleThreadModel接口,此時容器可以為每個容器的JVM實例化多個Servlet實例。
?
2.2.1 關(guān)于 Single Thread Model
SingleThreadModel 接口的作用是保證一個特定servlet實例的service方法在一個時刻僅能被一個線程執(zhí)行,一定要注意,此保證僅適用于每一個servlet實例,因此容器可以選擇池化這些對象。有些對象可以在同一時刻被多個servlet實例訪問,如HttpSession實例,可以在一個特定的時間對多個Servlet可用,包括那些實現(xiàn)了SingleThreadModel接口的Servlet。
?
2.3 Servlet生命周期
Servlet是按照一個嚴(yán)格定義的生命周期被管理,該生命周期規(guī)定了Servlet如何被加載、實例化、初始化、處理客戶端請求,以及何時結(jié)束服務(wù)。該聲明周期可以通過javax.servlet.Servlet 接口中的init、service和destroy這些API來表示,所有Servlet必須直接或間接的實現(xiàn)GenericServlet或HttpServlet抽象類。
?
2.3.1 加載和實例化
Servlet容器負(fù)責(zé)加載和實例化Servlet。加載和實例化可以發(fā)生在容器啟動時,或者延遲初始化直到容器決定有請求需要處理時。當(dāng)Servlet引擎啟動后,servlet容器必須定位所需要的Servlet類。Servlet容器使用普通的Java類加載設(shè)施加載Servlet類。可以從本地文件系統(tǒng)或遠(yuǎn)程文件系統(tǒng)或者其他網(wǎng)絡(luò)服務(wù)加載。加載完Servlet類后,容器就可以實例化它并使用了。
?
2.3.2 初始化
一旦一個Servlet對象實例化完畢,容器接下來必須在處理客戶端請求之前初始化該Servlet實例。初始化的目的是以便Servlet能讀取持久化配置數(shù)據(jù),初始化一些代價高的資源(比如JDBC API 連接),或者執(zhí)行一些一次性的動作。容器通過調(diào)用Servlet實例的init方法完成初始化,init方法定義在Servlet接口中,并且提供一個唯一的ServletConfig接口實現(xiàn)的對象作為參數(shù),該對象每個Servlet實例一個。
配置對象允許Servlet訪問由Web應(yīng)用配置信息提供的鍵-值對的初始化參數(shù)。該配置對象也提供給Servlet去訪問一個ServletContext對象,ServletContext描述了Servlet的運行時環(huán)境。請參考第4章,“Servlet Context”獲取ServletContext接口的更多信息。
?
2.3.2.1 初始化時的錯誤條件
在初始化階段,servlet實現(xiàn)可能拋出UnavailableException或ServletException異常。在這種情況下,Servlet不能放置到活動服務(wù)中,而且Servlet容器必須釋放它。如果初始化沒有成功,destroy方法不應(yīng)該被調(diào)用。
在實例初始化失敗后容器可能再實例化和初始化一個新的實例。此規(guī)則的例外是,當(dāng)拋出的UnavailableException表示一個不可用的最小時間,容器在創(chuàng)建和初始化一個新的servlet實例之前必須等待一段時間。
2.3.2.2使用工具時的注意事項
當(dāng)一個工具加載并內(nèi)省某個Web應(yīng)用程序時觸發(fā)的靜態(tài)初始化,這種用法與調(diào)用init初始化方法是有區(qū)別的。在Servlet的init方法沒被調(diào)用,開發(fā)人員不應(yīng)該假定其處于活動的容器環(huán)境內(nèi)。比如,當(dāng)某個Servlet僅有靜態(tài)方法被調(diào)用時,不應(yīng)該與數(shù)據(jù)庫或企業(yè)級JavaBean(EJB)容器建立連接。
?
2.3.3 請求處理
Servlet完成初始化后,Servlet容器就可以使用它處理客戶端請求了。客戶端請求由ServletRequest類型的request對象表示。Servlet封裝響應(yīng)并返回給請求的客戶端,該響應(yīng)由ServletResponse類型的response對象表示。這兩個對象(request和response)是由容器通過參數(shù)傳遞到Servlet接口的service方法的。
在HTTP請求的場景下,容器提供的請求和響應(yīng)對象具體類型分別是HttpServletRequest 和 HttpServletResponse。
需要注意的是,由Servlet容器初始化的某個Servlet 實例在服務(wù)期間,可以在其生命周期中不處理任何請求。
?
2.3.3.1 多線程問題
Servlet容器可以并發(fā)的發(fā)送多個請求到Servlet的service方法。為了處理這些請求,Servlet開發(fā)者必須為service方法的多線程并發(fā)處理做好充足的準(zhǔn)備。一個替代的方案是開發(fā)人員實現(xiàn)SingleThreadModel接口,由容器保證一個service方法在同一個時間點僅被一個請求線程調(diào)用,但是此方案是不推薦的。Servlet容器可以通過串行化訪問Servlet的請求,或者維護(hù)一個Servlet實例池完成該需求。如果Web應(yīng)用中的Servlet被標(biāo)注為分布式的,容器應(yīng)該為每一個分布式應(yīng)用程序的JVM維護(hù)一個Servlet實例池。
對于那些沒有實現(xiàn)SingleThreadModel 接口的Servlet,但是它的service方法(或者是那些HttpServlet中通過service方法分派的doGet、doPost等分派方法)是通過synchronized關(guān)鍵詞定義的,Servlet容器不能使用實例池方案,并且只能使用序列化請求進(jìn)行處理。強烈推薦開發(fā)人員不要去通過service方法(或者那些由Service分派的方法),因為這將嚴(yán)重影響性能。
?
2.3.3.2 請求處理時的異常
Servlet在處理一個請求時可能拋出ServletException或UnavailableException異常。ServletException表示在處理請求時出現(xiàn)了一些錯誤,容器應(yīng)該采取適當(dāng)?shù)拇胧┣謇淼暨@個請求。
UnavailableException表示servlet目前無法處理請求,或者臨時性的或者永久性的。
如果UnavailableException表示的是一個永久性的不可用,Servlet容器必須從服務(wù)中移除這個Servlet,調(diào)用它的destroy方法,并釋放Servlet實例。所有被容器拒絕的請求,都會返回一個SC_NOT_FOUND (404) 響應(yīng)。
如果UnavailableException表示的是一個臨時性的不可用,容器可以選擇在臨時不可用的這段時間內(nèi)路由任何請求到Servlet。所以在這段時間內(nèi)被容器拒絕的請求,都會返回一個SC_SERVICE_UNAVAILABLE (503)響應(yīng)狀態(tài)碼,且同時會返回一個Retry-After頭指示此Servlet什么時候可用。容器可以選擇忽略永久性和臨時性不可用的區(qū)別,并把UnavailableExceptions視為永久性的,從而Servlet拋出UnavailableException后需要把它從服務(wù)中移除。
?
2.3.3.3 異步處理
有時候,F(xiàn)ilter及/或 Servlet在生成響應(yīng)之前必須等待一些資源或事件以便完成請求處理。比如,Servlet在進(jìn)行生成一個響應(yīng)之前可能等待一個可用的JDBC連接,或者一個遠(yuǎn)程web服務(wù)的響應(yīng),或者一個JMS消息,或者一個應(yīng)用程序事件。在Servlet中等待是一個低效的操作,因為這是阻塞操作,從而白白占用一個線程或其他一些受限資源。許多線程為了等待一個緩慢的資源比如數(shù)據(jù)庫經(jīng)常發(fā)生阻塞,可能引起線程饑餓,且降低整個Web容器的服務(wù)質(zhì)量。當(dāng)
Servlet 3.0引入了異步處理請求的能力,使線程可以返回到容器,從而執(zhí)行更多的任務(wù)。當(dāng)開始異步處理請求時,另一個線程或回調(diào)可以或者產(chǎn)生響應(yīng),或者調(diào)用完成(complete)或請求分派(dispatch),這樣,它可以在容器上下文使用AsyncContext.dispatch方法運行。一個典型的異步處理事件順序是:
1. 請求被接收到,通過一系列如用于驗證的等標(biāo)準(zhǔn)的filter之后被傳遞到Servlet。
2. servlet處理請求參數(shù)及(或)內(nèi)容體從而確定請求的類型。
3. 該servlet發(fā)出請求去獲取一些資源或數(shù)據(jù),例如,發(fā)送一個遠(yuǎn)程web服務(wù)請求或加入一個等待JDBC連接的隊列。
4. servlet不產(chǎn)生響應(yīng)并返回。
5. 過了一段時間后,所請求的資源變?yōu)榭捎茫藭r處理線程繼續(xù)處理事件,要么在同一個線程,要么通過AsyncContext分派到容器中的一個資源上。
?
Java企業(yè)版的功能,如第15.2.2節(jié),在第15-178頁的“Web應(yīng)用環(huán)境”和第15.3.1節(jié),在第15-180頁的“EJB調(diào)用的安全標(biāo)識傳播”,僅提初始化請求的線程執(zhí)行,或者請求經(jīng)過AsyncContext.dispatch方法被分派到容器。Java企業(yè)版的功能可能支持由AsyncContext.start(Runnable)方法使用其他線程直接操作響應(yīng)對象。
第八章描述的@WebServlet和@WebFilter注解有一個屬性——asyncSupported,boolean類型默認(rèn)值為false。當(dāng)asyncSupported設(shè)置為true,應(yīng)用通過執(zhí)行startAsync(見下文)可以啟動一個單獨的線程中進(jìn)行異步處理,并把請求和響應(yīng)的引用傳遞給這個線程,然后退出原始線程所在的容器。這意味著響應(yīng)將遍歷(相反的順序)與進(jìn)入時相同的過濾器(或過濾器鏈)。直到AsyncContext調(diào)用complete(見下文)時響應(yīng)才會被提交。如果異步任務(wù)在容器啟動的分派之前執(zhí)行,且調(diào)用了startAsync并返回給容器,此時應(yīng)用需負(fù)責(zé)處理請求和響應(yīng)對象的并發(fā)訪問。
從一個Servlet分派時,把asyncSupported=true設(shè)置為false是允許的。這種情況下,當(dāng)servlet的service方法不支持異步退出時,響應(yīng)將被提交,且容器負(fù)責(zé)調(diào)用AsyncContext的complete,以便所有感興趣的AsyncListener得到觸發(fā)知。過濾器作為清理要完成的異步任務(wù)持有的資源的一種機制,也應(yīng)該使用AsyncListener. onComplete觸發(fā)的結(jié)果。
從一個同步Servlet分派到另一個異步Servlet是非法的。不過與該點不同的是當(dāng)應(yīng)用調(diào)用startAsync時將拋出IllegalStateException。這將允許servlet只能作為同步的或異步的Servlet。
應(yīng)用在一個與初始請求所用的不同的線程中等待異步任務(wù)直到可以直接寫響應(yīng),這個線程不知道任何過濾器。如果過濾器想處理新線程中的響應(yīng),那就必須在處理進(jìn)入時的初始請求時包裝response,并且把包裝的response傳遞給鏈中的下一個過濾器,并最終交給Servlet。因此,如果響應(yīng)是包裝的(可能被包裝多次,每一個過濾器一次),并且應(yīng)用處理請求并直接寫響應(yīng),這將只寫響應(yīng)的包裝對象,即任何輸出的響應(yīng)都會由響應(yīng)的包裝對象處理。當(dāng)應(yīng)用在一個單獨的線程中讀請求時,寫內(nèi)容到響應(yīng)的包裝對象,這其實是從請求的包裝對象讀取,并寫到響應(yīng)的包裝對象,因此對包裝對象操作的所有輸入及(或)輸出將繼續(xù)存在。
?
如果應(yīng)用選擇這樣做的話,它將可以使用AsyncContext從一個新線程發(fā)起到容器資源的分派請求。這將允許在容器范圍內(nèi)使用像JSP這種內(nèi)容生成技術(shù)。
?
除了注解屬性外,我們還添加了如下方法/類:
■ ServletRequest
■ public AsyncContext startAsync(ServletRequest req, ServletResponse res)。這個方法的作用是將請求轉(zhuǎn)換為異步模式,并使用給定的請求及響應(yīng)對象和getAsyncTimeout返回的超時時間初始化它的AsyncContext。ServletRequest和ServletResponse參數(shù)必須是與傳遞給servlet的service或filter的doFilter方法相同的對象,或者是ServletRequestWrapper和ServletResponseWrapper子類的包裝對象。當(dāng)應(yīng)用退出service方法時,調(diào)用該方法必須確保response沒有被提交。當(dāng)調(diào)用返回的AsyncContext的AsyncContext.complete或AsyncContext超時并且沒有監(jiān)聽器處理超時時,它將被提交。異步超時定時器直到請求和它關(guān)聯(lián)的響應(yīng)從容器返回時才啟動。AsyncContext可以被異步線程用來寫響應(yīng),它也能用來通知沒有關(guān)閉和提交的響應(yīng)。
如果請求在不支持異步操作的servlet或filter范圍中調(diào)用startAsync,或者響應(yīng)已經(jīng)被提交或關(guān)閉,或者在同一個分派期間重復(fù)調(diào)用,這些是非法的。從調(diào)用startAsync返回的AsyncContext可以接著被用來進(jìn)行進(jìn)一步的異步處理。調(diào)用返回的AsyncContext 的hasOriginalRequestResponse()方法將返回false,除非傳過去的ServletRequest和ServletResponse參數(shù)是最原始的那個或不是應(yīng)用提供的包裝器。
在請求設(shè)置為異步模式后,在入站調(diào)用期間添加的一些請求及(或)響應(yīng)的包裝器可能需要在異步操作期間一直保持,并且它們關(guān)聯(lián)的資源可能也不會釋放,出站方向調(diào)用的所有過濾器可以以此作為一個標(biāo)志。
一個在入站調(diào)用期間的過濾器應(yīng)用的ServletRequestWrapper可以被出站調(diào)用的過濾器釋放,只有當(dāng)給定的ServletRequest是由AsyncContext初始化的且通過調(diào)用AsyncContext.getRequest()返回的,不包括之前說的ServletRequestWrapper。這規(guī)則同樣適用于ServletResponseWrapper實例。
?
■ public AsyncContext startAsync() 是一個簡便方法,使用原始請求和響應(yīng)對象用于異步處理。請注意,如果它們在你想調(diào)用此方法之前被包裝了,這個方法的使用者應(yīng)該刷出(flush)響應(yīng),確保數(shù)據(jù)寫到被包裝的響應(yīng)中沒有丟失。
?
■ public AsyncContext getAsyncContext() – 返回由startAsync 調(diào)用創(chuàng)建的或初始化的AsyncContext。如果請求已經(jīng)被設(shè)置為異步模式,調(diào)用getAsyncContext 是非法的。
?
■ public boolean isAsyncSupported() – 如果請求支持異常處理則返回true,否則返回false。一旦請求傳給了過濾器或servlet不支持異步處理(通過指定的注解或聲明),異步支持將被禁用。
?
■ public boolean isAsyncStarted() – 如果請求的異步處理已經(jīng)開始將返回true,否則返回false。如果這個請求自從被設(shè)置為異步模式后已經(jīng)使用任意一個AsyncContext.dispatch方法分派,或者成功調(diào)用了AsynContext.complete 方法,這個方法將返回false。
?
■ public DispatcherType getDispatcherType() – 返回請求的分派器(dispatcher)類型。容器使用請求的分派器類型來選擇需要應(yīng)用到請求的過濾器。只有匹配分派器類型和url模式(url pattern)的過濾器才會被應(yīng)用。允許一個過濾器配置多個分派器類型,過濾器可以根據(jù)請求的不同分派器類型處理請求。請求的初始分派器類型定義為DispatcherType.REQUEST 。使用RequestDispatcher.forward(ServletRequest, ServletResponse) 或 RequestDispatcher.include(ServletRequest, ServletResponse) 分派時,它們的請求的分派器類型分別是DispatcherType.FORWARD 或 DispatcherType.INCLUDE ,當(dāng)一個異步請求使用任意一個AsyncContext.dispatch方法分派時該請求的分派器類型是DispatcherType.ASYNC。最后,由容器的錯誤處理機制分派到錯誤頁面的分派器類型是DispatcherType.ERROR 。
?
■ AsyncContext – 該類表示在ServletRequest啟動的異步操作執(zhí)行上下文,AsyncContext由之前描述的ServletRequest.startAsync創(chuàng)建并初始化。AsyncContext 的方法:
■ public ServletRequest getRequest() – 返回調(diào)用startAsync用于初始化AsyncContext 的請求對象。當(dāng)在異步周期之前調(diào)用了complete或任意一個dispatch方法,調(diào)用getRequest將拋出IllegalStateException。???
?
■ public ServletResponse getResponse() –返回調(diào)用startAsync用于初始化AsyncContext 的響應(yīng)對象。當(dāng)在異步周期之前調(diào)用了complete或任意一個dispatch方法,調(diào)用getResponse將拋出IllegalStateException。???
?
■ public void setTimeout(long timeoutMilliseconds) – 設(shè)置異步處理的超時時間,以毫秒為單位。該方法調(diào)用將覆蓋容器設(shè)置的超時時間。如果沒有調(diào)用setTimeout 設(shè)置超時時間,將使用容器默認(rèn)的超時時間。一個小于等于0的數(shù)表示異步操作將永不超時。當(dāng)調(diào)用任意一個ServletRequest.startAsync方法時,一旦容器啟動的分派返回到容器,超時時間將應(yīng)用到AsyncContext。當(dāng)在異步周期開始時容器啟動的分派已經(jīng)返回到容器后,再設(shè)置超時時間是非法的,這將拋出一個IllegalStateException異常。
?
■ public long getTimeout() – 獲取AsyncContext關(guān)聯(lián)的超時時間的毫秒值。該方法返回容器默認(rèn)的超時時間,或最近一次調(diào)用setTimeout設(shè)置超時時間。
?
■ public void addListener(AsyncListener listener, ServletRequest req, ServletResponse res) – 注冊一個用于接收的onTimeout, onError, onComplete 或 onStartAsync通知的監(jiān)聽器。前三個是與最近通過調(diào)用任意ServletRequest.startAsync方法啟動的異步周期相關(guān)聯(lián)的。onStartAsync 是與通過任意ServletRequest.startAsync 啟動的一個新的異步周期相關(guān)聯(lián)的。異步監(jiān)聽器將以它們添加到請求時的順序得到通知。當(dāng)AsyncListener得到通知,傳入到該方法的請求響應(yīng)對象與AsyncEvent.getSuppliedRequest()和AsyncEvent.getSuppliedResponse()是完全相同的。不應(yīng)該對這些對象進(jìn)行讀取或?qū)懭耄驗樽詮淖粤薃syncListener后可能發(fā)生了額外的包裝,不過可以被用來按順序釋放與它們關(guān)聯(lián)的資源。容器啟動的分派在異步周期啟動后返回到容器后,或者在一個新的異步周期啟動之前,調(diào)用該方法是非法的,將拋出IllegalStateException。
?
■ public <T extends AsyncListener> createListener(Class<T> clazz) – 實例化指定的AsyncListener類。返回的AsyncListener實例在使用下文描述的addListener方法注冊到AsyncContext之前可能需要進(jìn)一步的自定義。給定的AsyncListener類必須定義一個用于實例化的空參構(gòu)造器,該方法支持適用于AsyncListener的所有注解。
?
■ public void addListener(AsyncListener) – 注冊給定的監(jiān)聽器用于接收onTimeout, onError, onComplete 或 onStartAsync通知。前三個是與最近通過調(diào)用任意ServletRequest.startAsync方法啟動的異步周期相關(guān)聯(lián)的。onStartAsync 是與通過任意ServletRequest.startAsync 啟動的一個新的異步周期相關(guān)聯(lián)的。異步監(jiān)聽器將以它們添加到請求時的順序得到通知。當(dāng)AsyncListener接收到通知,如果在請求時調(diào)用startAsync(req, res) 或startAsync(),從AsyncEvent會得到同樣的請求和響應(yīng)對象。請求和響應(yīng)對象可以是或者不是被包裝的。異步監(jiān)聽器將以它們添加到請求時的順序得到通知。容器啟動的分派在異步周期啟動后返回到容器后,或者在一個新的異步周期啟動之前,調(diào)用該方法是非法的,將拋出IllegalStateException。
?
■ public void dispatch(String path) – 將用于初始化AsyncContext的請求和響應(yīng)分派到指定的路徑的資源。該路徑以相對于初始化AsyncContext 的ServletContext進(jìn)行解析。與請求查詢方法相關(guān)的所有路徑,必須反映出分派的目標(biāo),同時原始請求的URI,上下文,路徑信息和查詢字符串都可以從請求屬性中獲取,請求屬性定義在9-98頁的9.7.2章節(jié),“分派的請求參數(shù)”。這些屬性必須反映最原始的路徑元素,即使在多次分派之后。
?
■ public void dispatch() – 一個簡便方法,使用初始化AsyncContext 時的請求和響應(yīng)進(jìn)行分派,如下所示。 如果使用startAsync(ServletRequest, ServletResponse)初始化AsyncContext,且傳入的請求是HttpServletRequest的一個實例,則使用HttpServletRequest.getRequestURI()返回的URI進(jìn)行分派。否則分派的是容器最后分派的請求URI。下面的代碼示例2-1,代碼示例2-2和代碼示例2-3演示了不同情況下分派的目標(biāo)URI是什么。
代碼示例2-1:
// 請求到 /url/A
AsyncContext ac = request.startAsync();
...
ac.dispatch(); // 異步分派到 /url/A
???
代碼示例 2-2 :
// 請求到 /url/A
// 轉(zhuǎn)發(fā)到 /url/B
request.getRequestDispatcher(“/url/B”).forward(request, response);
// 從FORWARD的目標(biāo)內(nèi)啟動異步操作
AsyncContext ac = request.startAsync();
ac.dispatch(); // 異步分派到 /url/A
?
代碼示例2-3:
// 請求到 /url/A
// 轉(zhuǎn)發(fā)到 /url/B
request.getRequestDispatcher(“/url/B”).forward(request, response);
// 從FORWARD的目標(biāo)內(nèi)啟動異步操作
AsyncContext ac = request.startAsync(request, response);
ac.dispatch(); //異步分派到 /url/B
??
?
■ public void dispatch(ServletContext context, String path) -將用于初始化AsyncContext的請求和響應(yīng)分派到指定ServletContext的指定路徑的資源。
?
■ 之上定義了dispatch 方法的全部3個變體,調(diào)用這些方法且將請求和響應(yīng)對象傳入到容器的一個托管線程后將立即返回,在托管線程中異步操作將被執(zhí)行。請求的分派器類型設(shè)置為異步(ASYNC)。不同于RequestDispatcher.forward(ServletRequest, ServletResponse) 分派,響應(yīng)的緩沖區(qū)和頭信息將不會重置,即使響應(yīng)已經(jīng)被提交分派也是合法的。控制委托給分派目標(biāo)的請求和響應(yīng),除非調(diào)用了ServletRequest.startAsync() 或 ServletRequest.startAsync(ServletRequest, ServletResponse),否則響應(yīng)將在分派目標(biāo)執(zhí)行完成時被關(guān)閉。在調(diào)用了startAsync方法的容器啟動的分派沒有返回到容器之前任何dispatch方法的調(diào)用將沒有任何作用。AsyncListener.onComplete(AsyncEvent), AsyncListener.onTimeout(AsyncEvent)和AsyncListener.onError(AsyncEvent)的調(diào)用將被延遲到容器啟動的分派返回到容器之后。通過調(diào)用ServletRequest.startAsync.啟動的每個異步周期至多只有一個異步分派操作。相同的異步周期內(nèi)任何試圖執(zhí)行其他的異步分派操作是非法的并將導(dǎo)致拋出IllegalStateException。如果后來在已分派的請求上調(diào)用startAsync,那么所有的dispatch方法調(diào)用將和之上具有相同的限制。
?
■ 任何在執(zhí)行dispatch方法期間可能拋出的錯誤或異常必須由容器抓住和處理,如下所示:
i. 調(diào)用所有由AsyncContext創(chuàng)建的并注冊到ServletRequest的AsyncListener 實例的AsyncListener.onError(AsyncEvent) 方法, 可以通過AsyncEvent.getThrowable()獲取到捕獲的Throwable。
ii. 如果沒有監(jiān)聽器調(diào)用AsyncContext.complete 或任何AsyncContext.dispatch 方法,然后執(zhí)行一個狀態(tài)碼為HttpServletResponse.SC_INTERNAL_SERVER_ERROR的出錯分派,并且可以通過RequestDispatcher.ERROR_EXCEPTION請求屬性獲取Throwable值。
iii. 如果沒有找到匹配的錯誤頁面,或錯誤頁面沒有調(diào)用AsyncContext.complete() 或任何AsyncContext.dispatch 方法,則容器必須調(diào)用AsyncContext.complete。
?
■ public boolean hasOriginalRequestAndResponse() – 該方法檢查AsyncContext 是否以原始的請求和響應(yīng)對象調(diào)用ServletRequest.startAsync()完成初始化的,或者是否通過調(diào)用ServletRequest.startAsync(ServletRequest, ServletResponse)完成初始化的,且傳入的ServletRequest 和ServletResponse 參數(shù)都不是應(yīng)用提供的包裝器,這樣的話將返回true。如果AsyncContext 使用包裝的請求及(或)響應(yīng)對象調(diào)用ServletRequest.startAsync(ServletRequest, ServletResponse)完成初始化,那么將返回false。在請求處于異步模式后,該信息可以被出站方向調(diào)用的過濾器使用,用于決定是否在入站調(diào)用時添加的請求及(或)響應(yīng)包裝器需要在異步操作期間被維持或者被釋放。
?
■ public void start(Runnable r) – 該方法導(dǎo)致容器分派一個線程,該線程可能來自托管的線程池,用于運行指定的Runnable對象。容器可能傳播相應(yīng)的上下文信息到該Runnable 對象。
?
■ public void complete() – 如果調(diào)用了request.startAsync,則必須調(diào)用該方法以完成異步處理并提交和關(guān)閉響應(yīng)。如果請求分派到一個不支持異步操作的Servlet,或者由AsyncContext.dispatch調(diào)用的目標(biāo)servlet之后沒有調(diào)用startAsync,則complete方法會由容器調(diào)用。這種情況下,容器負(fù)責(zé)當(dāng)servlet的service方法一退出就調(diào)用complete()。 如果startAsync 沒有被調(diào)用則必須拋出IllegalStateException。在調(diào)用ServletRequest.startAsync() 或ServletRequest.startAsync(ServletRequest, ServletResponse) 之后且在調(diào)用任意dispatch方法之前的任意時刻調(diào)用complete()是合法的。在調(diào)用了startAsync方法的容器啟動的分派沒有返回到容器之前該方法的調(diào)用將沒有任何作用。AsyncListener.onComplete(AsyncEvent)的調(diào)用將被延遲到容器啟動的分派返回到容器之后。
?
■ ServletRequestWrapper
■ public boolean isWrapperFor(ServletRequest req)- 檢查該包裝器是否遞歸的包裝了給定的ServletRequest,如果是則返回true,否則返回false。
?
■ ServletResponseWrapper
■ public boolean isWrapperFor(ServletResponse res)- 檢查該包裝器是否遞歸的包裝了給定的ServletResponse,如果是則返回true,否則返回false。
?
■ AsyncListener
■ public void onComplete(AsyncEvent event) – 用于通知監(jiān)聽器在Servlet上啟動的異步操作完成了。
?
■ public void onError(AsyncEvent event) – 用于通知監(jiān)聽器異步操作未能完成。
?
■ public void onStartAsync(AsyncEvent event) – 用于通知監(jiān)聽器正在通過調(diào)用一個ServletRequest.startAsync方法啟動一個新的異步周期。正在被重新啟動的異步操作對應(yīng)的AsyncContext可以通過調(diào)用給定的event上調(diào)用AsyncEvent.getAsyncContext獲取。
?
■ 在異步操作超時的情況下,容器必須按照如下步驟運行:
?
■ 當(dāng)異步操作啟動后調(diào)用注冊到ServletRequest的所有AsyncListener實例的AsyncListener.onTimeout 方法。
?
■ 如果沒有監(jiān)聽器調(diào)用AsyncContext.complete() 或任何AsyncContext.dispatch 方法,執(zhí)行一個狀態(tài)碼為HttpServletResponse.SC_INTERNAL_SERVER_ERROR出錯分派。
?
■ 如果沒有找到匹配的錯誤頁面,或者錯誤頁面沒有調(diào)用AsyncContext.complete() 或任何AsyncContext.dispatch 方法,則容器必須調(diào)用AsyncContext.complete()。
?
■ 默認(rèn)情況下是不支持JSP中的異步處理,因為它是用于內(nèi)容生成且異步處理可能在內(nèi)容生成之前已經(jīng)完成。這取決于容器如何處理這種情況。一旦完成了所有的異步活動,使用AsyncContext.dispatch分派到的JSP頁面可以用來生成內(nèi)容。
?
■ 下面所示的圖2-1描述了各種異步操作的狀態(tài)轉(zhuǎn)換。
?
2.3.3.4 線程安全
除了startAsync 和complete 方法,請求和響應(yīng)對象的實現(xiàn)都不保證線程安全。這意味著它們應(yīng)該僅在請求處理線程范圍內(nèi)使用或應(yīng)用確保線程安全的訪問請求和響應(yīng)對象。
如果應(yīng)用使用容器管理對象創(chuàng)建一個線程,例如請求或響應(yīng)對象,這些對象必須在其生命周期內(nèi)被訪問,就像3.10和5.6章節(jié)定義的那樣。請注意,除了startAsync和complete方法,請求和響應(yīng)對象不是線程安全的。如果這些對象需要多線程訪問,需要同步這些訪問或通過包裝器添加線程安全語義,比如,同步化調(diào)用訪問請求屬性的方法,或者在線程內(nèi)為響應(yīng)對象使用一個局部輸出流。
?
2.3.3.5 升級處理
在HTTP/1.1,Upgrade通用頭(general-header)允許客戶端指定其支持和希望使用的其他通信協(xié)議。如果服務(wù)器找到合適的切換協(xié)議,那么新的協(xié)議將在之后的通信中使用。Servlet容器提供了HTTP升級機制。不過,Servlet容器本身不知道任何升級協(xié)議。協(xié)議處理封裝在協(xié)議處理器。在容器和協(xié)議處理器之間通過字節(jié)流進(jìn)行數(shù)據(jù)讀取或?qū)懭搿?
當(dāng)收到一個升級(upgrade)請求,servlet可以調(diào)用HttpServletRequest.upgrade方法啟動升級處理。應(yīng)用準(zhǔn)備和發(fā)送一個合適的響應(yīng)到客戶端。退出servlet service方法之后,servlet容器完成所有過濾器的處理并標(biāo)記連接已交給協(xié)議處理器處理。然后調(diào)用協(xié)議處理器的init方法,傳入一個WebConnection以允許協(xié)議處理器訪問數(shù)據(jù)流。
Servlet過濾器僅處理初始的HTTP請求和響應(yīng),然后它們將不會再參與到后續(xù)的通信中。換句話說,一旦請求被升級,它們將不會被調(diào)用。
協(xié)議處理器(ProtocolHandler)可以使用非阻塞IO(non blocking IO)消費和生產(chǎn)消息。
?
2.3.4 終止服務(wù)(End of Service)
Servlet容器沒必要保持裝載的Servlet持續(xù)任何特定的一段時間。一個Servlet實例可能會在servlet容器內(nèi)保持活躍(active)持續(xù)一段時間(以毫秒為單位),Servlet容器的壽命可能是幾天,幾個月,或幾年,或者是任何之間的時間。
當(dāng)Servlet容器確定servlet應(yīng)該從服務(wù)中移除時,將調(diào)用Servlet接口的destroy方法以允許Servlet釋放它使用的任何資源和保存任何持久化的狀態(tài)。例如,當(dāng)想要節(jié)省內(nèi)存資源或它被關(guān)閉時,容器可以做這個。
在servlet容器調(diào)用destroy方法之前,它必須讓當(dāng)前正在執(zhí)行service方法的任何線程完成執(zhí)行,或者超過了服務(wù)器定義的時間限制。
?
一旦調(diào)用了servlet實例的destroy方法,容器無法再路由其他請求到該servlet實例了。如果容器需要再次使用該servlet,它必須用該servlet類的一個新的實例。在destroy方法完成后,servlet容器必須釋放servlet實例以便被垃圾回收。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

