Servlet Interface 是Java Servlet API的核心抽象。所有的servlets都直接或者通過繼承一個類來實現這個接口。Java Servlet API 中 GeneticServlet 和 HttpServlet這兩個類實現了這個Servlet。大多數場景下,開發者只需繼承HttpServlet來實現它們的servlets。
1. Request Handling Methods
基本的Servlet接口定義了一個service方法來處理客戶端請求。這個方法每個請求都會執行,并且servlet容器把每個請求路由到一個servlet實例。
Web應用中的并發請求的處理通常需要Web的開發者在并發執行service的時候考慮如何處理多線程問題。
通常Web容器通過在不同線程并發執行service方法來處理并發請求到同一個servlet。
1.1 HTTP Specific Request Handling Methods
HttpServlet抽象子類在基礎的Servlet接口之外增加了額外的方法,它們會被service自動調用來幫助處理基于HTTP的請求。
- doGet來處理HTTP GET請求。
- doPost處理HTTP POST請求。
- doPut處理HTTP PUT 請求。
- doDelete處理HTTP DELETE請求。
- doHead 處理HTTP HEAD請求。
- doOptions 處理HTTP OPTIONS請求。
- doTrace 處理HTTP TRACE請求。
1.2 額外的方法
doPut和doDelete方法允許Servlet開發者支持需要這些特性的HTTP/1.1客戶端。HttpServlet中的doHead方法是doGet的一種特殊格式,它僅返回doGet返回數據的頭部數據 。doOptions方法返回servlet支持的方法。doTrace方法在TRACE請求中生成包含所有headers實例的響應。
1.3 有條件的GET支持
HttpServlet接口定義了一個getLastModified方法來支持有條件的GET操作。一個有條件的GET操作僅請求自上次特定時間以來修改過的資源。實現這些方法有助于高效利用網絡資源。
2. 實例數量
Servlet的聲明會控制servlet容器提供怎樣的servlet實例,這些聲明包括annotation,部署描述符。
對于不是部署在分布式環境中的servlet,servlet容器對于每一個servlet聲明對應一個實例。然而對于實現了SingleThreadModel接口的servlet,servlet容器可以初始化多個實例來處理大量的請求負載和把請求序列化為一個特殊的實例。
在通過部署描述符部署的分布式應用中,servlet部署為應用的一部分,一個JVM中,容器僅可以一個servlet聲明對應一個實例。然而,如果servlet在分布式應用中實現了SingleThreadModel接口,容器在每個JVM中會實現多個實例。
2.1 單線程模型的注意點
SingleThreadModel接口的使用確保在一個給定servlet實例的service方法中,一個時刻僅有一個線程在執行。值得注意的是這種保證僅適用于每個servlet實例,因為容器可以選擇緩存對象。這些能在同一時刻獲取一個以上的servlet的對象,比如HttpSession實例,在任一時刻對多個servlet都是可用的,包括實現SingleThreadModel接口的servlet。
推薦開發者采用其它方式來解決問題,而不是實現這個接口,比如避免使用實例變量或者使用同步塊來獲取資源。這個SingleThreadModel接口在此版本中將不建議使用。
2.2 Servlet生命周期
Servlet通過定義良好的生命周期來管理。生命周期決定servlet何時加載、初始化、處理客戶端請求、退出service。生命周期通過javax.servlet.Servlet接口的API (init, service, destroy) 方法來實現。所有的servlet必須直接或者間接地通過GenericServlet或者HttpServlet抽象類來實現這三個接口。
2.2.1 加載&實例化
Servlet容器負責加載和實例化servlets。加載和實例化動作可能在容器啟動時發生,或者推遲到容器需要使用servlets的時候。
當Servlets引擎啟動,必要的Servlets類必須被容器初始化。Servlets容器用普通Java類的加載方式來加載Servlets。它們可能從本地文件系統、遠端文件系統或者其它網絡服務加載。
在Servlets類加載后,容器把它們實例化好,以備后續使用。
2.2.2 初始化
在Servlets實例化后,容器必須在它處理客戶端請求之前初始化Servlets。初始化讓Servlets能讀取持久化配置信息,初始化耗時資源 (如 JDBC 連接), 以及執行其他一次性動作。容器通過調用Servlet接口的init方法,并傳遞給init一個實現了ServletConfig接口的唯一對象來初始化servlet實例。這個配置對象允許servlet獲取Web應用配置信息中的name-value初始化參數。這個配置對象也允許servlet獲取描述servlet運行時環境的對象(實現了ServletsContext接口)。
2.2.2.1 初始化中的錯誤條件
在初始化期間,servlet實例能拋出一個UnavailableException或者ServletException。這種情況下,servlet一定不能放進活躍服務中,且必須在容器中被釋放掉。destroy方法不會被調用,因為它被認為是失敗的初始化過程。
在初始化失敗后,一個新的實例可以被容器實例化&初始化。這個規則有個例外,就是當UnavailableException異常明確指出一個最小不可用時間時,容器必須等待這段時間過去了,再初始化一個新的servlet實例。
2.2.2.2 工具考慮
當一個工具調用init方法來加載并運行一個web應用時,靜態初始化方法的觸發十分重要。直到servlet接口的init方法被調用,開發者才應該假設一個servlet在活躍的容器運行時之中。比如,一個servlet在僅僅調用靜態方法時,不應該建立DB鏈接或者企業JavaBean容器。
2.2.3 請求處理
在一個servlet被初始化好之后,servlet容器就可以用它來處理客戶端請求。請求通過ServletRequest類型的請求對象到達服務。servlet通過調用ServletResponse類型對象的方法來填充response對象。這些對象被作為參數傳遞給servlet接口的service方法。
在一個HTTP請求過程中,容器提供的對象是HttpServletRequest和HttpServletResponse類型。
需要注意的是,一個被容器放進service中的servlet實例在它的生命周期中可以不處理請求。
2.2.3.1 多線程問題
一個servlet容器可能通過servlet的service方法發送多個并發請求。處理請求過程中,servlet開發者必須在service方法中處理好多線程的并發執行過程。
盡管不推薦,但是一個替代方案是開發者實現SingleThreadModel接口,讓容器來保證一個時刻,service方法中僅有一個線程。servlet容器通過在servlet上排序請求來滿足這個需求,或者維護一個servlet實例池。如果servlet是分布式應用的一部分,容器可以在每一個應用部署所在的JVM中維護一個servlet實例池。
對那些沒有實現SingleThreadModel接口的servlets,如果service方法 (或者方法,如doGet,doPost) 定義是帶有synchronized關鍵字,容器不能使用實例池這種方式,但必須讓請求有序。由于性能的原因,強烈建議開發者不要使用為service方法加上同步關鍵字。
2.2.3.2 處理請求過程中的異常
一個servlet在請求處理過程中可能拋出ServletException或者UnavailableException。一個ServletException表明在處理請求過程中出現了一些錯誤,并且容器需要采取措施來清理這個請求。
一個UnavailableException表明一個servlet暫時或者永遠不能處理請求。
如果UnavailableException提示永久不可用,容器必須從服務中移除這個servlet,調用它的destroy方法,釋放servlet實例。任何由于這個原因被拒絕的服務必須返回404.
如果UnavailableException提示暫時不可用,容器可以在這段不可用時段內選擇不分發任何請求。任何在這段時間被拒絕的請求必須返回帶有503的response,并且帶著Retry-After頭。
容器也可以不區分永久性和暫時性,把它們全部視為永久不可用,因此需要移除哪些拋UnavailableException的servlet。
2.2.2.3.3 異步處理
有時候,一個filter或者servlet在產生response之前沒有等到一個資源或者事件,它不能完成請求的處理。比如,在繼續生成response之前,一個servlet可能需要等待一個可用的連接、遠程服務的結果、JMS消息或者一個應用事件。在一個servlet中等待是效率很低的事情,因為這種阻塞操作會消耗線程和其它有限的資源。一個被高頻調用的資源,比如數據庫連接可能讓很多線程阻塞等待獲取連接,這就會造成線程饑餓以及整個web容器的低性能。
servlet3.0引入了異步處理請求的能力,線程可以返回給容器接著執行其它任務。一個請求上,當異步處理開始,其它線程或者回調可以生成response并調用complete,或者把請求分發出去,讓請求通過AsyncContext.dispatch方法,可以運行在容器的上下文中。一個典型的異步處理事件順序如下:
- 1.Request被接受,并且通過普通filters的鑒權等動作之后傳遞給servlet。
- 2.servlet處理請求參數或者內容來決定Request的類型。
- 3.servlet給請求分配資源、數據,比如發送一個遠程web服務或者加入隊列等待JDBC連接。
- 4.servlet返回,并不生成response。
- 5.一些時間之后,請求的資源就緒后,處理那個事件的線程繼續在同一個線程執行或者在容器中使用AsyncContext分配一個資源。
Java EE的某些特征僅僅對于那些執行初始請求的線程可用,或者通過AsyncContext.dispatch方法把請求分發給容器。
Java EE特征對另外一些通過AsyncContext.start(Runnable)方法直接在response操作的線程可用。
@WebServlet和@WebFilter注解有一個屬性--asyncSupported(擁有默認值為false的)。當asyncSupported被置為true,應用可以調用startAsync在單獨的線程里面啟動異步處理,傳遞一個執行Request和response對象的應用給它,然后從容器中最初的線程中退出。這也就是說response將穿過與此相反的filter鏈。response直到AsyncContext的complete方法被調用才提交。如果異步任務在調用startAsync,已初始化的分發已經返回給容器之前執行那么應用負責處理對Request和response的并發訪問。
把一個asyncSupported置為true的servlet分發給asyncSupport置為false的servlet是可以的。這種場景下,當不支持async的servlet的service方法退出,response將退出。并且容器負責調用AsyncContext的complete以便那些感興趣的AsyncListener實例被通知到。AsyncListener.onComplete通知也能被filters利用來清理擁有的資源,以讓異步任務順利完成。
從一個異步servlet分發到一個異步servlet是非法的。然而拋IllegalStateException的決定與應用調用startAsync的時間點不同。這允許一個servlet作為異步或者同步的servlet。
應用等待的異步任務可能在不同的線程,而不是最初的請求中寫response。這個線程對filter一無所知。如果filter想操作新線程中的response,它必須在處理原始請求時包裝response,并且把包裝的response傳遞給filter鏈的下一個,最后傳遞給servlet。如果response被包裝,應用處理請求并且直接寫回response。這里其實真正寫的是包裝類,比如任何加到response的輸出仍然要被response的包裝類處理。當應用在單獨線程中讀request時,并把輸出添加到response,其實它真正地是從request包裝類中讀,往response的包裝類中寫,因此任何包裝類的輸出和輸出將繼續執行。
應用可以使用AsyncContext來把請求從新線程中分發給容器中的一個資源,如果它選擇這么做。這能讓在容器范圍內使用JSP這類內容生成技術可行。
除了注解屬性,我們還有下面的方法或者類:
- ServletRequest
-
public AsyncContext startAync(ServletRequest req, ServletResponse res). 這個方法把請求放進異步模式中,而且用request和response對象和getAsyncTimeout返回的超時時間來初始化AsynContext。ServletRequest和ServletResponse參數必須與調用servlet的service的參數、filter的doFilter方法或者ServletRequestWrapper和ServletResponseWrapper的包裝類的子類一致。調用這個方法可以確保在應用退出service方法時,response不會提交。response將在調用AsyncContext的AsyncContext.complete或者AsyncContext超時并且沒有處理超時的監聽者時提交。異步時間的計時器直到請求和相關的response已經從容器返回時才開始計時。AsyncContext能用來在異步線程中寫response。它也能用來通知--response沒有關閉和提交。
如果請求在不支持異步操作的servlet或者filter的scope中,或者response被提交或者關閉,又或者在相同的分發過程中再次被調用,這些情況下去調用startAsync是非法的。調用startAsync時返回的AsyncContext稍后能被進一步的異步處理使用。調用AsyncContext.hasOriginalRequestResponse() 將返回false,除非被傳遞的ServletRequest和ServletResponse參數是原始對象或者沒有攜帶應用提供的包裝類。在請求放進異步模式之后,任何在外部方向的被調用的filter能使用這個返回值來作為提示:一些在它們內部調用的時期添加的一部分Request和response包裝類可能需要在異步處理期間保留,并且它們相關的資源可能不會被釋放。僅當初始化AsyncContext并且調用AsyncContext.getRequest()返回的ServletRequest不包含所謂的ServletRequestWrapper,應用在一個filter的內部調用期間的ServletRequestWrapper可以通過外部調用釋放。對于ServletResponseWrapper實例也是一樣。
public AsyncContext startAsync() 方法被提供來作為使用原始Request和response對象來處理異步請求的方便方法。值得注意的是,這個方法的調用者在調用之前應該flush包裝過的response,以確保寫到包裝response的數據不會丟失。
public AsyncContext getAsyncContext() - 返回調用startAsync創建或者重新初始化的AsyncContext對象。如果請求沒有被放進異步模式中,調用getAsyncContext是非法行為。
public boolean isAsyncSupported() -如果請求支持異步處理返回true,否則返回false。當請求被傳遞給不支持異步(通過委托注解或者聲明)的servlet或者filter,Async支持功能會被disable。
public boolean isAsyncStarted() - 如果異步處理開始了返回true,否則返回false。如果在它被放進異步模式或者AsyncContext.complete被調用的時候請求通過AsyncContext.dispatch方法分發,這個方法返回false。
public DispatcherType getDispatcherType() - 返回請求的分發器類型。容器根據分發器的類型來選擇需要應用在Request上的filter。僅符合分發器類型和url模式的filter會被應用到。允許為多個分發器類型配置好的filter查詢它請求的分發器類型能讓filter根據分發器類型對請求做不同的處理。請求的初始化分發類型是DispatcherType.REQUEST。
通過RequestDispatcher.forward(ServletRequest, ServletResponse) 或者 RequestDispatcher.include(ServletRequest, ServletResponse) 分發的分發類型是DispatcherType.FORWARD或者DispatcherType.INCLUDE,然而通過AsyncContext.dispatch方法分發的分發器類型是DispatcherType.ASYNC。最后請求的分發器類型被容器的錯誤處理器分發到錯誤頁面,它就是DispatcherType.ERROR。
- AsyncContext - 這個類代表了在ServletRequest之上開始的異步操作的執行上下文。AsyncContext通過上文描述符的ServletRequest.startAsync調用來創建和初始化。下面是一些在AsyncContext中的方法:
- public ServletRequest getRequest() - 返回用來初始化調用startAsync方法返回的AsyncContext對象的request對象。
- public ServletResponse getResponse() - 返回用來初始化調用startAsync方法返回的AsyncContext對象的response對象。
- public void setTimeout(long timeoutMilliseconds) - 給異步處理過程設置超時時間,單位是毫秒。調用這個方法會覆蓋容器設置的超時時間。如果超時時間沒有通過調用setTimeout設置,將會采用容器默認值。小于等于0的值表示異步操作從不超時。一旦已初始化的容器在任一被調用的ServletRequest.startAsync方法返回給容器期間分發出去,超時間就在AsyncContext上生效。如果這個方法在已初始化容器在已開始的異步周期之后返回給容器,那么設置超時間將導致IllegalStateException.
- public long getTimeout() - 獲取與AsyncContext相關的超時時間,單位為毫秒。這個方法返回容器默認的超時時間或者通過setTimeout方法設置的超時時間。
- public void addListener(asyncListener, req, res) - 給通過調用ServletRequest.startAsync方法的異步周期中的超時,錯誤和complete的通知注冊監聽者。異步監聽者會按照添加到請求對象中的順序被通知。當AsyncListener被通知,被傳遞給這個方法的request和response對象與AsyncEvent.getSuppliedRequest()和AsyncEvent.getSuppliedResponse()返回的對象一致。這些對象不應該被讀或者寫,因為額外的包裝在AsyncListener注冊時可能出現,可能被用來按順序釋放資源。如果這個方法在已初始化容器在已開始的異步周期之后返回給容器,或者一個新的異步周期開始之前,那么此調用將導致IllegalStateException。
- public <T extends AsyncListener> createListener(Class<T> claszz) - 實例化AsyncListener類。返回的AsyncListener實例在通過調用addListener返回的AsyncContext注冊之前還可以進一步定制。AsyncListener類必須實現無參構造函數用來初始化實例。這個方法支持應用到AsyncListener上的任一注解。
- public void addListener(asyncListener) - 給通過調用ServletRequest.startAsync方法的異步周期中的超時,錯誤和complete的通知注冊監聽者。如果請求中startAsync(req, res)或者 startAsync()被調用,且當AsyncListener被通知到,相同的request和response也能從AsyncEvent中獲取。request和response可能被包裝過,也可能沒有被包裝過。異步監聽器將會按照他們添加進請求的順序被通知。如果這個方法在已初始化容器在已開始的異步周期之后返回給容器,或者一個新的異步周期開始之前,那么此調用將導致IllegalStateException。
- public void dispatch(path) - 把用來初始化AsyncContext的request和response對象分發給指定路徑的資源。指定路徑是相對于初始化AsyncContext的ServletContext。所有request查詢方法相關的路徑必須反映出分發目標,雖然原始的請求URI,上下文路徑,路徑信息和查詢字符串可以從request屬性中獲取。即使是多次分發以后,這些屬性必須要反應原始路徑元素。
-
public void dispatch() - 提供一個方便的方法來分發用來初始化AsyncContext的request和response對象。
如果AsyncContext通過startAsync(ServletRequest, ServletResponse)初始化并且傳遞的請求對象是HttpServletRequest實例,那么分發就是HttpServletRequest.getRequestURI()返回的URI。否則分發就是上次容器分發的請求對象的URI。下面的例子code example1-1, code example1-2, code example1-3說明了不同場景下的分發目標URI:
CODE EXAMPLE1-1:
REQUEST to /url/A
AsyncContext ac = request.startAsync();
...
ac.dispatch(): dose a ASYNC dispatch to /url/A
CODE EXAMPLE1-2:
REQUEST to /url/A
REQUEST to /url/B
getRequestDispatcher("/url/B").forward(request, response);
AsyncContext ac = request.startAsync();
...
ac.dispatch(): dose a ASYNC dispatch to /url/A
CODE EXAMPLE1-3:
REQUESR to /url/A
REQUESR to /url/B
getRequestDispatcher("/url/B").forward(request, response);
AsyncContext ac = request.startAsync(request, response);
...
ac.dispatch(): dose a ASYNC dispatch to /url/B
public void dispatch(ServletContext context ,String path) - 把用來初始化AsyncContext的request和response對象分發給ServletContext中指定路徑相關的資源上。
對于上面定義的有不同變量的3個dispatch方法,調用這個方法,把request和response對象傳遞給容器管理的線程(分發動作會在此線程中執行)就立即返回。請求對象的分發器類型被置為ASYNC。
不像RequestDispatcher.forward(ServletRequest, ServletResponse)分發,response對象的緩存和頭信息不會被重置,即使response對象已經被提交,分發它也是合法的。在request和response對象之上的控制被委托給分發目標,當分發對象完成執行,response對象將會關閉,除非ServletRequest.startAsync()或者ServletRequest.startAsync(ServletRequest, ServletResponse)被調用。如果任何分發方法在已初始化的容器分發startAsync調用返回容器之前被調用,那么這個調用在已初始化分發返回容器之后才生效。調用AsyncListener.onComplete(AsyncEvent), AsyncListener.onTimeout(AsyncEvent)和AsyncListener.onError(AsyncEvent)將會推遲,直到已初始化容器分發已經返回給容器。在你每一個通過ServletRequest.startAsync方法調用開始的異步周期,最多有一個異步分發操作。在同一個異步周期內試圖執行額外的異步分發操作是非法的,并且會導致IllegalStateException。如果startAsync在分發請求之后調用,任何一個dispatch方法調用都有上述同樣的限制。-
任何在dispatch方法執行過程中會出現的錯誤或者異常必須被容器按下述方式捕獲并處理。
- 為所有用ServletRequest注冊的AsyncListener實例觸發AsyncListener.onError(AsyncEvent)方法。
AsyncContext被創建并且通過AsyncEvent.getThrowable()準備好Throwable異常。 - 如果沒有監聽者調用AsyncContext.complete或者任何一個AsyncContext.dispatch方法,那么用錯誤碼 (HttpServletResponse.SC_INTERNAL_SERVER_ERROR) 來執行錯誤dispatch, 并且把與請求對象中相等的RequestDispatcher.ERROR_EXCEPTION屬性值--Throwable準備好。
- 如果沒有匹配的錯誤頁面,或者錯誤頁面并沒有調用AsyncContext.complete(),和任一一個AsyncContext.dispatch 方法,那么容器必須調用AsyncContext.complete.
- 為所有用ServletRequest注冊的AsyncListener實例觸發AsyncListener.onError(AsyncEvent)方法。
public boolean hasOriginalRequestAndResponse() - 這個方法檢查調用ServletRequest.startAsync()返回的AsyncContext是否使用原始request和response對象初始化,或者它是否調用ServletRequest.startAsync(ServletRequest, ServletResponse)來初始化以及ServletRequest和ServletResponse參數不攜帶任何應用提供的包裝層,這種情況將會返回true。如果AsyncContext使用方法ServletRequest.startAsync(ServletRequest, ServletResponse),并用request和response包裝對象作為參數來初始化,它返回false。這些信息將在內部順序中被filter使用來決定在內部調用(需要在異步操作期間保留或者釋放)期間它們添加的是否是request和response包裝對象。
public void start(Runnable r) - 這個方法讓容器分發一個線程(很可能從線程池中獲取)來運行指定的Runnable。容器可以傳遞合適的上下文信息給Runnable。
public void complete() - 如果Request.startAsync被調用,那么這個方法必須被調用來完成異步處理并提交、關閉response。如果request被分發給不支持異步過程的servlet,或者被AsyncContext.dispatch調用但并不繼續調用startAsync的目標servlet,complete方法能被容器調用。這個case中一旦servlet的service退出,容器負責調用complete方法。如果startAsync沒有被調用,IllegalStateException必須拋出。在調用ServletRequest.startAsync()或者ServletRequest.startAsync(ServletRequest, ServletResponse)之后,且調用一個dispatch方法之前,任何時間調用此方法都是合法的。如果這個方法在已初始化同期調用startAsync的分發返回給容器之前調用,那么直到已經初始化的分發返回給容器之后本次調用才生效。AsyncListener.onComplete(AsyncEvent)也將會延遲觸發,直到已經初始化的分發返回給容器之后。
- ServletRequestWrapper
- public boolean isWrapperFor(ServletRequest req) - 遞歸檢查包裝器是否包裝了給定ServletRequest,如果是返回true,否則返回false。
- ServletResponseWrapper
- public boolean isWrapperFor(ServletResponse req) - 遞歸檢查包裝器是否包裝了給定ServletResponse,如果是返回true,否則返回false。
- AsyncListener
public void onComplete(AsyncEvent event) - 用來通知在ServletRequest開始的異步操作完成的監聽者。
public void onTimeout(AsyncEvent event) - 用來通知在ServletRequest開始的異步操作超時的監聽者。
public void onError(AsyncEvent event) - 用來通知在ServletRequest開始的異步操作錯誤的監聽者。
public void onStartAsync(AsyncEvent event) - 用來通知調用任意一個ServletRequest.startAsync方法調用后,一個新的異步周期初始化的監聽者。對應正在重新初始化的異步操作AsyncContext可以通過在給定的event對象中調用AsyncEvent.getAsyncContext獲得。
-
在異步操作超時的事件中,容器必須執行以下步驟:
- 在ServletRequest上異步操作初始化期間注冊的所有監聽者實例上調用AsyncListener.onTimeout方法。
- 如果沒有監聽者調用AsyncContext.complete()方法或者任何一個AsyncContext.dispatch方法,那么用與HttpServletResponse.SC_INTERNAL_SERVER_ERROR等值的狀態碼去執行一次錯誤分發。
- 如果沒有找到匹配的錯誤頁面,或者錯誤頁面沒有調用AsyncContext.complete() 或者任何AsyncContext.dispatch方法,那么容器必須調用AsyncContext.complete()。
JSP中的異步處理默認不會被支持,因為它用于內容生成并且異步處理必須在內容生成之前完成。這取決于容器怎樣處理這種情況。一旦所有的異步處理結束,使用AsyncContext.dispatch分發給JSP頁面能被用來生成內容。
下圖1-4是各種不同異步操作的狀態轉換的圖形說明。
2.3.3.4 線程安全
不像startAsync和complete函數,request和response對象的實現并不被保證線程安全。這就意味著他們應該只在請求處理的線程scope中或者應用能保證線程安全地獲取request和response的情況下使用。
如果應用創建的線程使用容器管理的對象,比如request或者response對象,那些對象必須保證僅在他們的生命周期中被獲取。需要注意的是startAsync和complete方法是線程安全的,但是request和response對象不是線程安全的。如果它們在多線程中被獲取,獲取過程需要被同步或者通過添加線程安全的包裝類來實現。比如同步獲取request屬性獲取的方法,或者在線程中為response對象使用本地輸出流。
2.3.4 服務結束
容器并不要求在任意特殊時段保持一個servlet被加載。一個servlet實例在容器里保持活躍,可以是在毫秒時段中,可以是在servlet容器的生命周期中,也可以兩者間的任意時間段。
當servlet決定一個servlet應該從服務中移除,它調用Servlets接口的destroy方法來讓一個servlet釋放它持有的任何資源和保存任何持久化狀態。比如,當它想保存內存資源或者關閉的時候就可以調用destroy。
在servlet容器調用destroy方法之前,它必須讓servlet的service方法里并發執行的任何線程執行完畢或者超出服務器設定的時間限制。
一旦servlet實例上的destroy方法被調用,容器不再把請求分發給servlet實例。如果容器需要再次使用servlet,它必須用此servlet類的新實例。
在destroy方法完成后,servlet容器必須釋放servlet實例以便后面的GC。
翻譯自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010