Servlet3.0提供的異步處理

    在之前的Servlet規範中,若是Servlet做爲控制器調用了一個耗時的業務方法,那麼Servlet必須等到業務方法徹底返回以後纔會生成響應,這將使得Servlet對業務方法的調用變成一種阻塞式的調用,所以效率比較低。Servlet3.0規範引入了異步處理來解決這個問題,異步處理容許Servlet從新發起一條線程去調用耗時的業務方法,這樣就能夠避免等待。html

    Servlet3.0的異步處理是經過AsyncContext類來處理的,Servlet可經過ServletRequest的以下兩個方法來開啓異步調用、建立AsyncContext對象:java

  • AsyncContext startAsync()web

  • AsyncContext startAsync(ServletRequest, ServletResponse)瀏覽器

    重複調用上面的方法將獲得同一個AsyncContext對象。AsyncContext對象表明異步處理的上下文,它提供了一些工具方法,可完成設置異步調用的超時時長,dispatch用於請求、啓動後臺線程、獲取request,response對象等功能,下面是一個進行異步處理的Servlet類:session

@WebServlet(urlPatterns="/async", asyncSupported=true)
public class AsyncServlet extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    response.setContentType("text/html;charset=GBK");
    PrintWriter out = response.getWriter();
    out.println("進入Servlet的時間:" + new Date() + ".<br/>");
    out.flush();
    AsyncContext acontext = request.startAsync();
    acontext.setTimeout(20*1000);
    acontext.start(new Executor(acontext));
    out.println("結束Servlet的時間:" + new Date() + ".<br/>");
    out.flush();
  }
}

 

public class Executor implements Runnable {
  private AsyncContext context;
  public Executor(AsyncContext context) {this.context = context;}
  public void run(){
    try {
      Thread.sleep(5000);
      ServletRequest request = context.getRequest();
      List<String> books = new ArrayList<String>();
      books.add("book1"); books.add("book2"); books.add("book3");
      request.setAttribute("books", books);
      context.dispatch("/async.jsp");
    } catch (Exception e) {
      e.printStachTrace();
    }
  }
}

    在此Executor中,讓線程睡眠5秒來模擬調用的耗時的業務方法,最後調用AsyncContext的dispatch方法把請求轉發到指定的JSP頁面。被異步請求的JSP頁面須要指定session="false",代表該頁面不會從新建立Session,下面是async.jsp的內容:app

<%@ page contentType="text/html;chaset=GBK" language="java" session="fasle" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<ul>
  <c:forEach items="${books}" var="book" >
    <li>${book}</li>
  </c:forEach>
</ul>
<%
  out.println("業務調用結束的時間:" + new Date());
  request.getAsyncContext().complete();//完成異步調用
%>

    上面的頁面只是一個JSP頁面,只是使用了JSTL標籤庫來迭代輸出books集合,所以須要將JSTL的兩個Jar包複製到項目的lib目錄中。異步

對於但願啓用異步調用的Servlet而言,開發者必須顯示指定開啓異步調用,爲Servlet開啓異步調用有兩種方式:1). 在@WebServlet中指定asyncSupported=true  2). 在web.xml的<servlet>元素中增長<async-supported>子元素,如下是一個配置片斷:jsp

<servlet>
  <servlet-name>async</servlet-name>
  <servlet-class>com.abc.AsyncServlet</servlet-class>
  <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
  <servlet-name>async</servlet-name>
  <url-pattern>/async</url-pattern>
</servlet-mapping>

    對於支持異步調用的Servlet來講,當Servlet以異步方式啓用新線程後,該Servlet的執行不會被阻塞,該Servlet將能夠向客戶端瀏覽器生成相應——當新線程執行完成後,新線程生成的相應將再次被送往客戶端瀏覽器。async

    當Servlet啓用異步調用的線程以後,該線程的執行過程對開發者是透明的。但在有些狀況下,開發者須要瞭解該異步線程的執行細節,並針對特定的執行結果進行鍼對性處理,這能夠藉助於Servlet3.0提供的異步監聽器來實現。異步監聽器須要實現AsyncListener接口,該接口中定義了以下方法:工具

  • onStartAsync(AsyncEvent event):當異步調用開始時觸發該方法

  • onComplete(AsyncEvent event):當異步調用結束時觸發該方法

  • onError(AsyncEvent event):當異步調用出錯時觸發該方法

  • onTimeout(AsyncEvent event):當異步調用超時時觸發該方法

    下面是一個簡單的監聽器類:

public class MyAsyncListener implements AsyncListener {
  public void onComplete(AsyncEvent event) {
    System.out.println("異步調用完成:" + new Date());
  }
  public void onError(AsyncEvent event) {
    System.out.println("異步調用出錯:" + new Date());
  }
  public void onStartAsync(AsyncEvent event) {
    System.out.println("異步調用開始:" + new Date());
  }
  public void onTimeout(AsyncEvent event) {
    System.out.println("異步調用超時:" + new Date());
  }
}

提供了監聽器以後,還須要經過AsynContext來註冊此監聽器,調用該對象的addListener()方法便可完成監聽器的註冊:

AsyncContext acontext = request.startAsync();
acontext.addListener(new MyAsyncListener());
相關文章
相關標籤/搜索