聶永的博客

          記錄工作/學習的點點滴滴。

          Servlet 3.0筆記之異步攔截器(async filter)的學習

          異步Servlet有時需要一個攔截器,但必須是異步的Filter,否則將會報錯:
          嚴重: Servlet.service() for servlet [com.learn.servlet3.async.DemoAsyncLinkServlet] in context with path [/servlet3] threw exceptionjava.lang.IllegalStateException: Not supported.
          因此異步的Filter攔截異步Servlet,不要搞錯。
          我們需要預先定義這么一個異步連接,每秒輸出一個數字字符串,從0到99,諸如下面HTML字符串:
          <div>2</div>
          最后輸出Done!
          給出兩個訪問地址,一個用于被攔截(/demoAsyncLink),一個用于單獨訪問(/demoAsyncLink2),便于對照:
          /**
          * 模擬長連接實現,每秒輸出一些信息
          *
          * @author yongboy
          * @date 2011-1-14
          * @version 1.0
          */
          @WebServlet(
          urlPatterns = { "/demoAsyncLink", "/demoAsyncLink2" },
          asyncSupported = true
          )
          public class DemoAsyncLinkServlet extends HttpServlet {
          private static final long serialVersionUID = 4617227991063927036L;

          protected void doGet(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {
          response.setHeader("Cache-Control", "private");
          response.setHeader("Pragma", "no-cache");
          response.setHeader("Connection", "Keep-Alive");
          response.setHeader("Proxy-Connection", "Keep-Alive");
          response.setContentType("text/html;charset=UTF-8");

          PrintWriter out = response.getWriter();
          out.println("<div>Start ...</div>");
          out.flush();

          AsyncContext asyncContext = request.startAsync(request, response);

          new CounterThread(asyncContext).start();
          }

          private static class CounterThread extends Thread {
          private AsyncContext asyncContext;
          public CounterThread(AsyncContext asyncContext) {
          this.asyncContext = asyncContext;
          }

          @Override
          public void run() {
          int num = 0;
          int max = 100;
          int interval = 1000;

          // 必須設置過期時間,否則將會出連接過期,線程無法運行完畢異常
          asyncContext.setTimeout((max + 1) * interval);
          PrintWriter out = null;

          try {
          try {
          out = asyncContext.getResponse().getWriter();
          } catch (IOException e) {
          e.printStackTrace();
          }

          while (true) {
          out.println("<div>" + (num++) + "</div>");
          out.flush();

          if (num >= max) {
          break;
          }

          Thread.sleep(interval);
          }
          } catch (InterruptedException e) {
          e.printStackTrace();
          }

          if (out != null) {
          out.println("<div>Done !</div>");
          out.flush();
          out.close();
          }

          asyncContext.complete();
          }
          }
          }
          若想讓HttpServletResponse包裝器發揮包裝的效果,須調用帶有參數的startAsync(request, response)方法開啟異步輸出,否則MarkWapperedResponse將不起作用。因為,若不傳遞現有的request,response對象,將會調用原生的request和response對象。
          在tomcat7下面,異步連接超時時間為10000單位,若不指定超時時間,遞增的數字不會按照預想完整輸出到99。
          我們假設需要定義這樣一個Filter,為每一次的異步輸出的內容增加一個特殊標記:
          <!--marked filter-->
          <div>2</div>
          邏輯很簡單,作為示范也不需要多復雜。
          再看看一個異步Filter的代碼:
          /**
          * 異步攔截器
          *
          * @author yongboy
          * @date 2011-1-14
          * @version 1.0
          */
          @WebFilter(
          dispatcherTypes = {
          DispatcherType.REQUEST,
          DispatcherType.FORWARD,
          DispatcherType.INCLUDE
          },
          urlPatterns = { "/demoAsyncLink" },
          asyncSupported = true //支持異步Servlet
          )
          public class AsyncServletFilter implements Filter {
          private Log log = LogFactory.getLog(AsyncServletFilter.class);

          public AsyncServletFilter() {
          }

          public void destroy() {
          }

          public void doFilter(ServletRequest request, ServletResponse response,
          FilterChain chain) throws IOException, ServletException {

          log.info("it was filted now");

          MarkWapperedResponse wapper = new MarkWapperedResponse(
          (HttpServletResponse) response);

          chain.doFilter(request, wapper);
          }

          public void init(FilterConfig fConfig) throws ServletException {
          }
          }
          很簡單,添加上asyncSupported = true屬性即可。在上面Filter中包裝了一個HttpServletResponse對象,目的在于返回一個定制的PrintWriter對象,簡單重寫flush方法(不見得方法多好):
          /**
          * HttpServletResponse簡單包裝器,邏輯簡單
          *
          * @author yongboy
          * @date 2011-1-14
          * @version 1.0
          */
          public class MarkWapperedResponse extends HttpServletResponseWrapper {
          private PrintWriter writer = null;
          private static final String MARKED_STRING = "<!--marked filter--->";

          public MarkWapperedResponse(HttpServletResponse resp) throws IOException {
          super(resp);

          writer = new MarkPrintWriter(super.getOutputStream());
          }

          @Override
          public PrintWriter getWriter() throws UnsupportedEncodingException {
          return writer;
          }

          private static class MarkPrintWriter extends PrintWriter{

          public MarkPrintWriter(OutputStream out) {
          super(out);
          }

          @Override
          public void flush() {
          super.flush();

          super.println(MARKED_STRING);
          }
          }
          }
          在瀏覽器端請求被包裝的/demoAsyncLink鏈接,截圖以及firebug檢測截圖如下:
           
          可以在瀏覽器內同時請求/demoAsyncLink2前后作為對比一下。

          posted on 2011-01-15 19:39 nieyong 閱讀(8156) 評論(0)  編輯  收藏 所屬分類: Servlet3

          公告

          所有文章皆為原創,若轉載請標明出處,謝謝~

          新浪微博,歡迎關注:

          導航

          <2011年1月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          統計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 当阳市| 重庆市| 个旧市| 沙坪坝区| 沈丘县| 富阳市| 昭觉县| 会昌县| 丹凤县| 文水县| 从化市| 三穗县| 湟中县| 临潭县| 阿坝| 大城县| 石景山区| 勃利县| 淮安市| 沽源县| 迁西县| 江孜县| 墨竹工卡县| 淮滨县| 含山县| 平潭县| 河西区| 罗田县| 启东市| 嵩明县| 保康县| 竹北市| 武汉市| 蒙自县| 淳安县| 泰和县| 水富县| 道真| 沽源县| 卢湾区| 远安县|