在早前版本的Servlet规范中,如果Servlet作为控制器调用了一个较耗时的业务方法,那么Servlet必须等到业务方法完全返回之后才生成响应,这使得Servlet对业务方法的调用变成一种阻塞式的调用,从而导致运行效率降低。
Servlet3.0规范引入了异步处理来解决这个问题,异步处理允许Servlet重新发起一条新线程去调用较耗时的业务方法,这样就可避免等待。该异步机制是通过AsyncContext类来处理的,Servlet可通过ServletRequest的如下两个方法开启异步调用、创建AsyncContext对象:
1、AsyncContext startAsync()
2、AsyncContext startAsync(ServletRequest,ServletResponse)
重复调用上面的方法将得到同一个AyncContext对象,AyncContext对象代表异步处理的上下文,它提供了一些工具方法,可以完成设置异步调用的超时时长,dispatch用于请求,启动后台线程、获取request response对象等功能
Code demo:
@WebServlet(urlPatterns="/async",asyncSupported=true)
public class AsyncServlet extends HttpServlet
{
@Override
public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{
response.setContentType("text/html,charset=GBK");
PrinterWriter out = response.getWriter();
out.println("<title>异步</tilte>");
out.println("进入Servlet的时间:"+new java.util.Date()+".<br/>");
out.flush();
// 创建AsyncContext,开始异步调用
AsyncContext actx = request.startAsync();
// 设置异步调用的超时时长
actx.setTimeout(30*1000);
// 启动异步调用的线程
actx.start(new Executor(actx));
out.println("结束Servlet的时间:"+new java.util.Date()+".<br />");
out.flush();
}
}
Excutor.java:
public class Executor implements Runnable
{
pirvate AyncContext actx = null;
public Executor(AsyncContext){ this.actx = actx; }
public void run()
{
try{
// 等待10秒,模拟业务方法的执行
Thread.sleep(10 * 1000);
ServletRequest request = actx.getRequest();
List<String> books = new ArrayList<>();
books.add("Thinking in java");
books.add("Effective java");
books.add("Core Java");
request.setAttribute("books",books);
actx.dispatch("/async.jsp");
}catch(Exception e){ e.printStackTrace(); }
}
}
该线程执行体内让线程暂停10秒钟来模拟调用耗时的业务方法,最后调用 AsyncContext的dispatch方法把请求dispatch到指定的JSP页面。
被 异步请求dispatch的目标页面需要指定session="false",表明该页面不会重新创建 session。
async.jsp:
<% page contentType="text/html;charset=GBK" language="java" session="false"%>
<% 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 java.util.Date());
request.getAsyncContext().complete();
%>