聶永的博客

          記錄工作/學(xué)習(xí)的點(diǎn)點(diǎn)滴滴。

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

          異步Servlet有時(shí)需要一個(gè)攔截器,但必須是異步的Filter,否則將會(huì)報(bào)錯(cuò):
          嚴(yán)重: Servlet.service() for servlet [com.learn.servlet3.async.DemoAsyncLinkServlet] in context with path [/servlet3] threw exceptionjava.lang.IllegalStateException: Not supported.
          因此異步的Filter攔截異步Servlet,不要搞錯(cuò)。
          我們需要預(yù)先定義這么一個(gè)異步連接,每秒輸出一個(gè)數(shù)字字符串,從0到99,諸如下面HTML字符串:
          <div>2</div>
          最后輸出Done!
          給出兩個(gè)訪問地址,一個(gè)用于被攔截(/demoAsyncLink),一個(gè)用于單獨(dú)訪問(/demoAsyncLink2),便于對(duì)照:
          /**
          * 模擬長連接實(shí)現(xiàn),每秒輸出一些信息
          *
          * @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;

          // 必須設(shè)置過期時(shí)間,否則將會(huì)出連接過期,線程無法運(yùn)行完畢異常
          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包裝器發(fā)揮包裝的效果,須調(diào)用帶有參數(shù)的startAsync(request, response)方法開啟異步輸出,否則MarkWapperedResponse將不起作用。因?yàn)椋舨粋鬟f現(xiàn)有的request,response對(duì)象,將會(huì)調(diào)用原生的request和response對(duì)象。
          在tomcat7下面,異步連接超時(shí)時(shí)間為10000單位,若不指定超時(shí)時(shí)間,遞增的數(shù)字不會(huì)按照預(yù)想完整輸出到99。
          我們假設(shè)需要定義這樣一個(gè)Filter,為每一次的異步輸出的內(nèi)容增加一個(gè)特殊標(biāo)記:
          <!--marked filter-->
          <div>2</div>
          邏輯很簡單,作為示范也不需要多復(fù)雜。
          再看看一個(gè)異步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中包裝了一個(gè)HttpServletResponse對(duì)象,目的在于返回一個(gè)定制的PrintWriter對(duì)象,簡單重寫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);
          }
          }
          }
          在瀏覽器端請(qǐng)求被包裝的/demoAsyncLink鏈接,截圖以及firebug檢測(cè)截圖如下:
           
          可以在瀏覽器內(nèi)同時(shí)請(qǐng)求/demoAsyncLink2前后作為對(duì)比一下。

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

          公告

          所有文章皆為原創(chuàng),若轉(zhuǎn)載請(qǐng)標(biāo)明出處,謝謝~

          新浪微博,歡迎關(guān)注:

          導(dǎo)航

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

          統(tǒng)計(jì)

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個(gè)人收藏

          最新隨筆

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 锦屏县| 昌乐县| 黄陵县| 双辽市| 漳平市| 体育| 连平县| 普陀区| 抚远县| 德兴市| 沅陵县| 开封市| 新干县| 普陀区| 买车| 扶风县| 盐亭县| 郁南县| 余庆县| 永福县| 凌云县| 平邑县| 德化县| 黔西| 湄潭县| 涟水县| 时尚| 资中县| 长白| 灵川县| 正宁县| 扶沟县| 奎屯市| 宁远县| 镇康县| 陵川县| 香河县| 吕梁市| 崇明县| 伊金霍洛旗| 枣阳市|