歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Servlet 3.0的AsyncListener接口

Servlet 3.0的AsyncListener接口

日期:2017/3/1 9:27:05   编辑:Linux編程

一、Servlet 3.0介紹
Servlet 3.0作為 JavaEE 6規范中一部分,隨著JavaEE 6規范一起發布。該版本在前一版本(Servlet 2.5)的基礎上提供了若干新特性用於簡化Web應用的開發和部署。其中有幾項特性的引入讓開發者感到非常興奮,同時也獲得了Java社區的一片贊譽之聲:
1)異步處理支持
在Servlet 3.0版本之前,Servlet線程需要一直阻塞,直到業務處理完畢才能再輸出響應,最後才結束該Servlet線程。而有了異步處理特性,Servlet線程不再需要一直阻塞,在接收到請求之後,Servlet線程可以將耗時的操作委派給另一個線程來完成,自己在不生成響應的情況下返回至容器。針對業務處理較耗時的情況,這可以大幅度降低服務器的資源消耗,並且提高並發處理速度。
2)新增的注解支持
Servlet 3.0版本新增了若干注解,用於簡化 Servlet、過濾器(Filter)和監聽器(Listener)的聲明,這使得web.xml部署描述文件不再是必選項了。
3)功能可插拔支持

二、服務器推技術
在Servlet 2.5中,頁面發送一次請求是順序執行的,即在Servlet的Service中開啟一個線程,線程處理後的結果是無法返回給頁面的,Servlet執行完畢後,Response就關閉了,無法將後台更新數據即時更新到頁面端。
服務器推技術的實現
1)定時發送請求,頁面有刷新,不友好
2)Ajax輪詢,然後通過js更新頁面數據
相比前者雖然友好,訪問量太大時,服務器會增加壓力,小型應用可以考慮用
3)反向Ajax(即Comet技術)
利用Http 1.1長連接的特性,即通過輪詢,但客戶端每次發送請求時服務器不會立即返回響應,而是等待服務器有新數據時才返回或者沒有新數據而連接超時返回。
相比於ajax輪詢,減少了服務端壓力,但一個缺點,不是所有浏覽器都支持。
在Servlet 3.0中提供了異步支持,當數據返回頁面後,Request並沒有關閉,當服務器端有數據更新時,就可以推送了。

三、Servlet 3.1規范的改進
GlassFish 4.0以上版本率先實現了Servlet 3.1規范。Servlet 3.1規范的新特性有:
1)更便利的注解支持
提供了@WebServlet、@WebFilter、@WebListener、@WebInitParam等注解的支持
2)可插拔的設計
Web模塊化:可以將一個項目分成N個模塊,然後通過掃描模塊下的META-INF/web-fragment.xml進行裝配。
容器啟動時的可插拔:使用ServletContainerInitializer實現,可以在容器啟動時自動回調其onStartup方法,插入一些功能。
零XML配置的類SpringMVC:使用ServletContainerInitializer即SpringMVC注解配置實現無XML化的SpringMVC配置。
3)異步處理支持
Servlet的異步支持:
- 通過Servlet提供的異步支持完成了Comet:Streaming(長連接)和Ajax長輪詢
- 使用Servlet提供的AsyncListener進行狀態回調
- 最後通過Ajax長輪詢實現了一個聊天室功能
SpringMVC對Servlet的異步支持:
- 使用SpringMVC框架提供的異步支持實現Comet:Streaming(長連接)和Ajax長輪詢
- 使用SpringMVC框架提供的Callable實現異步計算
- 使用SpringMVC框架提供的DeferredResult實現延遲結果(實現Ajax長輪詢)
- Spring框架沒有提供長連接實現,具體還得使用原生支持
- 最後通過ajax長輪詢實現了一個聊天室功能
4)非阻塞I/O
5)HTTP協議升級

四、Servlet的監聽器
Servlet的監聽器即Listener,它可以監聽來自客戶端的請求、完成服務器端的操作等。通過監聽器,可以自動觸發一些操作,比如:監聽在線用戶的數量,當增加一個HttpSession時,就觸發一個sessionCreated(HttpSessionEvent se)方法,這樣就可以給在線人數加1了。
常用的監聽器接口有:
1. ServletContextListener
監聽ServletContext。當創建ServletContext時,觸發contextInitialized(ServletContextEvent sce)方法;當銷毀ServletContext時,觸發contextDestroyed(ServletContextEvent sce)方法。
2. ServletContextAttributeListener
監聽對ServletContext屬性的操作,比如增加、刪除、修改屬性。
3. HttpSessionListener
監聽HttpSession的操作。當創建一個Session時,觸發session Created(HttpSessionEvent se)方法;當銷毀一個Session時,觸發sessionDestroyed(HttpSessionEvent se)方法。
4. HttpSessionAttributeListener
監聽HttpSession屬性的操作。當在Session增加一個屬性時,觸發attributeAdded(HttpSessionBindingEvent se)方法;當在Session刪除一個屬性時,觸發attributeRemoved(HttpSessionBindingEvent se)方法;當在Session屬性被重新設置時,觸發attributeReplaced(HttpSessionBindingEvent se)方法。

Servlet 3.0的監聽器跟Servlet 2.5的監聽器差別不大,唯一的區別就是增加了對注解的支持。在3.0以前,監聽器的配置需要配置到web.xml文件。在3.0中,監聽器的配置既可以放入web.xml文件,還可以使用注解進行配置。對於使用注解的監聽器就是在監聽器類上使用@WebListener進行注釋,這樣Web容器就會把這個類當成是一個監聽器進行注冊和使用。
如果是采用web.xml配置文件的方式,那麼就是這樣:

<listener>
<listener-class>com.xxx.SessionListener</listener-class>
</listener>
<listener>
<listener-class>com.xxx.ContextListener</listener-class>
</listener>

五、Servlet 3.0的異步處理
有時Filter或Servlet在生成響應之前必須等待一些耗時的操作結果以便完成請求處理。而在Servlet中,等待是一個低效的操作,因為這是阻塞操作,會白白占用一個線程或其他一些受限資源。許多線程為了等待一個緩慢的資源(如數據庫連接)經常發生阻塞,可能引起線程饑餓,從而降低整個Web容器的服務質量。
Servlet 3.0引入了異步處理請求的能力,使線程可以返回到容器,從而執行更多的任務。當開始異步處理請求時,另一個線程或回調可以或產生響應,或調用完成(complete)或請求分派(dispatch)。這樣,它可以在容器上下文使用AsyncContext.dispatch方法運行。
一個典型的異步處理事件順序是:
1. 請求被接收到,通過一系列如用於驗證的標准Filter之後被傳遞到Servlet。
2. Servlet處理請求參數及內容體從而確定請求的類型。
3. 該Servlet發出請求去獲取一些資源或數據。
4. Servlet不產生響應並返回。
5. 過了一段時間後,所請求的資源變為可用,此時處理線程繼續處理事件,要麼在同一個線程,要麼通過AsyncContext分派到容器中的某個資源上。

@WebServlet注釋和@WebFilter注釋有一個屬性——asyncSupported,是布爾類型,默認值為false。當asyncSupported設置為true,則應用通過執行startAsync可以啟動一個單獨的線程進行異步處理,並把請求和響應的引用傳遞給這個線程,然後退出原始線程所在的容器。這意味著響應將遍歷(相反的順序)與進入時相同的過濾器(或過濾器鏈)。直到AsyncContext調用complete時響應才會被提交。如果異步任務在容器啟動的分派之前執行,且調用了startAsync並返回給容器,此時應用需負責處理請求和響應對象的並發訪問。
從一個 Servlet分派時,把asyncSupported=true設置為false是允許的。這種情況下,當Servlet的Service方法不支持異步退出時,響應會被提交,且容器負責調用AsyncContext的complete,以便所有感興趣的AsyncListener能得到觸發通知。過濾器作為清理要完成的異步任務持有資源的一種機制,也應該使用AsyncListener.onComplete觸發。
從一個同步Servlet分派到另一個異步Servlet是非法的。不過與該點不同的是當應用調用startAsync時將拋出IllegalStateException。這將允許Servlet只能作為同步的或異步的Servlet。
應用在一個與初始請求所用的不同的線程中等待異步任務直到可以直接寫響應,這個線程不知道任何過濾器。如果過濾器想處理新線程中的響應,那就必須在處理進入時的初始請求時包裝Response,並且把包裝的Response傳遞給鏈中的下一個過濾器,並最終交給Servlet。因此,如果響應是包裝的(可能被包裝多次,每一個過濾器一次),並且應用處理請求並直接寫響應,這將只寫響應的包裝對象,即任何輸出的響應都會由響應的包裝對象處理。
當應用在一個單獨的線程中讀請求時,寫內容到響應的包裝對象,這其實是從請求的包裝對象讀取,並寫到響應的包裝對象,因此對包裝對象操作的所有輸入或輸出將繼續存在。如果應用選擇這樣做的話,它將可以使用AsyncContext一個新線程發起到容器資源的分派請求。這將允許在容器范圍內使用像 JSP 這種內容生成技術。

六、異步監聽器接口AsyncListener
Servlet 3.0為異步處理提供了一個監聽器,使用AsyncListener接口表示。此接口負責管理異步事件,它可以監控如下四種事件:
1)異步線程開始時,調用AsyncListener的onStartAsync(AsyncEvent event)方法;
2)異步線程出錯時,調用AsyncListener的onError(AsyncEvent event)方法;
3)異步線程執行超時,則調用AsyncListener的onTimeout(AsyncEvent event)方法;
4)異步執行完畢時,調用AsyncListener的onComplete(AsyncEvent event)方法;

要注冊一個AsyncListener,只需將准備好的AsyncListener對象傳遞給AsyncContext對象的addListener()方法即可,如下所示:

  1. @Override
  2. protected void doGet(HttpServletRequest req, HttpServletResponse res){
  3. AsyncContext actx = req.startAsync();
  4. actx.addListener(new AsyncListener(){
  5. public void onComplete(AsyncEvent event) throws IOException{
  6. // do 一些清理工作或者其他
  7. }
  8. public void onTimeout(AsyncEvent event) throws IOException{
  9. // do 一些超時處理的工作或者其他
  10. }
  11. });
  12. ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
  13. executor.execute(new MyAsyncService(actx));
  14. }

在Servlet Filter中使用Spring容器 http://www.linuxidc.com/Linux/2015-06/118881.htm

Java Servlet關鍵點詳解 http://www.linuxidc.com/Linux/2015-06/118468.htm

Tomcat手工搭建Jsp和Servlet程序 http://www.linuxidc.com/Linux/2014-07/104282.htm

Copyright © Linux教程網 All Rights Reserved