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字符串:
我們需要預先定義這么一個異步連接,每秒輸出一個數字字符串,從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,為每一次的異步輸出的內容增加一個特殊標記:
在tomcat7下面,異步連接超時時間為10000單位,若不指定超時時間,遞增的數字不會按照預想完整輸出到99。
我們假設需要定義這樣一個Filter,為每一次的異步輸出的內容增加一個特殊標記:
<!--marked filter-->
<div>2</div>
邏輯很簡單,作為示范也不需要多復雜。
再看看一個異步Filter的代碼:
再看看一個異步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