一.  概述

  异步处理功能可以节约容器线程。你应该将此功能 使用在长时间运行的操作上。此功能的作用是释放正在 等待完成的线程,使该线程能够被另一请求所使用。

二. 编写异步Servlet和过滤器

  WebServlet和WebFilter注解类型可能包含新的 asyncSupport属性。要编写支持异步处理的Servlet或过 滤器,需设置asyncSupported属性为true:

@WebServlet(asyncSupported=true ...)
@WebFilter(asyncSupported=true ...)

  此外,也可以在部署文件里面指定这个描述符。例 如,下面的 Servlet 配置为支持异步处理:

<servlet>
<servlet-name>AsyncServlet</servlet-name>
<servlet-class>servlet.MyAsyncServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>

  Servlet或过滤器要支持异步处理,可以通过调用 ServletRequest的startAsync方法来启动一个新线程。这 里有两个startAsync的重载方法:

AsyncContext startAsync() throws java.lang.IllegalStateExceptio
n
AsyncContext startAsync(ServletRequest servletRequest,
ServletResponse servletResponse) throws
java.lang.IllegalStateException

  这两个重载方法都返回一个AsyncContext的实例, 这个实例提供各种方法并且包含ServletRequest和 ServletResponse。第一个重载实例比较简单并且使用方 便。由此生成的asynccontext实例将包含原生的 ServletRequest和ServletResponse。第二个允许您将原来 的ServletRequest和ServletResponse进行重写封装后传给 asynccontext。需要注意的是,你只能传递原生的 ServletRequest和ServletResponse或它们的封装到 startAsync第二种重载实例。

  注意,startAsync重复调用将返回相同的 asynccontext。若一个Servlet或过滤器调用startAsync时 不支持异步处理,将抛出java.lang.illegalstateexception 异常。还请注意,asynccontext的start方法是非阻塞的, 所以下一行代码仍将执行,即使还未调度线程启动。

三. 编写异步Servlets

  写一个异步或异步Servlet或过滤器比较简单。当有 一个需要相当长的时间完成的任务时,需要创建一个异 步的Servlet或过滤器。在异步Servlet或过滤器类中需要 做如下操作:

  (1)调用ServletRequest中的startAsync方法。该 startAsync返一个AsyncContext 。

  (2)调用AsyncContext的setTimeout(),传递容器 等待任务完成的超时时间的毫秒数。此步骤是可选的, 但如果你不设置超时,容器的将使用默认的超时时间。 如果任务未能在指定的超时时间内完成,将会抛出一个 超时异常。

  (3)调用asyncContext.start,传递一个Runnable来 执行一个长时间运行的任务。

  (4)调用Runnable的asynccontext.complete或 asynccontext.dispatch方法来完成任务。

这里是一个异步Servlet的doGet或doPost方法的框 架:

final AsyncContext asyncContext = servletRequest.startAsync();
asyncContext.setTimeout( ... );
asyncContext.start(new Runnable() {
@Override
public void run() {
// long running task
asyncContext.complete() or asyncContext.dispatch()
}
})

  下面例子显示了支持异步处理的 Servlet。

AsyncDispatchServlet 类

package servlet;

import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @WebServlet(name = "AsyncDispatchServlet", urlPatterns = { "/asyncDispatch" }, asyncSupported = true)
public class AsyncDispatchServlet extends HttpServlet {
private static final long serialVersionUID = 222L; @Override
public void doGet(final HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync();
request.setAttribute("mainThread", Thread.currentThread().getName());
asyncContext.setTimeout();
asyncContext.start(new Runnable() {
@Override
public void run() {
// long-running task
try {
Thread.sleep();
} catch (InterruptedException e) {
}
request.setAttribute("workerThread", Thread.currentThread().getName());
// 调度到其它资源取完成任务 例如threadNames.jsp页面
asyncContext.dispatch("/threadNames.jsp");
}
});
}
}

这个Servlet支持异步处理且其长期运行的 任务就是简单地休眠三秒钟。为了证明这个长时间运行 的任务是在不同的线程中执行的,而不是在主线程中执 行的(即执行 Servlet的doGet方法),它将主线程的名 字和工作线程的ServletRequest分派到一个 threadNames.jsp页面。该threadNames.jsp页面显示mainThread和WorkerThread变量。它 们应打印不同的线程名字。

threadNames.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
mainThread: ${mainThread} <br />
workThread: ${workerThread} </body>
</html>

注意,你需要在任务结束后调用asynccontext的 dispatch或complete,所以它不会等待,直到它超时。

你可以把你的这个URL输入到浏览器来测试 servlet:

  上图显示了主线程的名称和工作线程的名称。你 在你的浏览器中看到的可能是不同的,但打印出的线程 名字会有所不同,证明了工作线程与主线程不同.

  除了调度到其他资源去完成任务,你也可以调用 AsyncContext的complete方法。此方法通知servlet容器 该任务已完成。

  作为第二个例子,思考一下清单  AsyncCompleteServlet Servlet。 该Servlet每秒发送一次进度更新,使用户能够监测进展 情况。它发送HTML响应和一个简单的JavaScript代码来 更新HTML div元素。

package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class AsyncCompleteServlet extends HttpServlet {
private static final long serialVersionUID = 78234L; @Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
final PrintWriter writer = response.getWriter();
writer.println("<html><head><title>" + "Async Servlet</title></head>");
writer.println("<body><div id='progress'></div>");
final AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout();
asyncContext.start(new Runnable() {
@Override
public void run() {
System.out.println("new thread:" + Thread.currentThread());
for (int i = ; i < ; i++) {
writer.println("<script>");
writer.println("document.getElementById(" + "'progress').innerHTML = '" + (i * ) + "% complete'");
writer.println("</script>");
writer.flush();
try {
Thread.sleep();
} catch (InterruptedException e) {
}
}
writer.println("<script>");
writer.println("document.getElementById(" + "'progress').innerHTML = 'DONE'");
writer.println("</script>");
writer.println("</body></html>");
asyncContext.complete();
}
});
}
}

部署描述符(web.xml文 件)

<servlet>
<servlet-name>AsyncComplete</servlet-name>
<servlet-class>servlet.AsyncCompleteServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>AsyncComplete</servlet-name>
<url-pattern>/async-supported</url-pattern>
</servlet-mapping>

四. 异步监听器

  为支持Servlet和过滤器配合执行异步操作,Servlet 3.0还增加了asynclistener接口用于接收异步处理过程中 发生事件的通知。AsyncListener接口定义了如下方法, 当某些事件发生时调用:

  

void onStartAsync(AsyncEvent event)

在异步操作启动完毕后调用该方法

void onComplete(AsyncEvent event)

在异步操作完成后调用该方法。

void onError(AsyncEvent event)

在异步操作失败后调用该方法.

void onTimeout(AsyncEvent event)

在异步操作超时后调用该方法,即当它未能在指定 的超时时间内完成时。

  所有四种方法可以分别通过它们的 getAsyncContext、getSuppliedRequest和 getSuppliedResponse方法,从AsyncContext、 ServletRequest、ServletResponse中获取相关的 AsyncEvent。

  这里有一个例子,MyAsyncListener类 实现AsyncListener接口,以便在异步操作事件发生时, 它能够得到通知。请注意,和其他网络监听器不同,你 不需要通过@WebListener注解来实现。

  

package listener;

import java.io.IOException;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener; // 不需要标注@WebListener
public class MyAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("onComplete");
} @Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("onError");
} @Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("onStartAsync");
} @Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("onTimeout");
}
}

  由于AsyncListener类不是用@WebListener注解的, 因此必须为AsyncContext手动注册一个AsyncListener监 听器,用于接收所需要的事件。通过调用addListener方 法为AsyncContext注册一个AsyncListener监听器:

 Class lClass;
try {
lClass = Class.forName("listener.MyAsyncListener");
AsyncListener al = asyncContext.createListener(lClass);
asyncContext.addListener(al);
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

使用asynclistener

package servlet;

import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import listener.MyAsyncListener; @WebServlet(name = "AsyncListenerServlet", urlPatterns = { "/asyncListener" }, asyncSupported = true)
public class AsyncListenerServlet extends HttpServlet {
private static final long serialVersionUID = 62738L; @Override
public void doGet(final HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(); Class lClass;
try {
lClass = Class.forName("listener.MyAsyncListener");
AsyncListener al = asyncContext.createListener(lClass);
asyncContext.addListener(al);
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep();
} catch (InterruptedException e) {
}
String greeting = "hi from listener";
System.out.println("wait....");
request.setAttribute("greeting", greeting);
asyncContext.dispatch("/test.jsp");
}
});
}
}

jsp 异步处理的更多相关文章

  1. ajax 异步 通信 小例子 servlet与 jsp异步 post方法

    post请求 url后面加参数 接收不到的,必须 放到send("use"=user)形式 还要加上 xhr.setRequestHeader("Content-Type ...

  2. ajax 异步 通信 小例子 servlet与 jsp异步 get

    get  请求参数通过 url那里写进去,然后send(null) html文件和 servlet进行通信 通过ajax 进行通信 <!DOCTYPE html PUBLIC "-// ...

  3. Filter 快速开始 异步Servlet 异步请求 AsyncContext 异步线程 异步派发 过滤器拦截

    [web.xml] <filter> <filter-name>normalFilter</filter-name> <filter-class>net ...

  4. url 处理

    一.jsp异步请求后台(servlet) 的url RegisterServlet  与 web.xml 的路径一样 function checkPhoneNumber(){ var phonenum ...

  5. Spring MVC(二)

    spring mvc工作流 1A)客户端发出http请求,只要请求形式符合web.xml 文件中配置的*.action的话,就由DispatcherServlet 来处理. 1B)Dispatcher ...

  6. Spring MVC 以.html为后缀名访问获取数据,报406 Not Acceptable错误。

    如题,最近以spring mvc作为后台框架,前端异步获取数据时(.html为后缀名的访问方式),报406 Not Acceptable错误.当初都不知道啥原因,前后台都没报错就是返回不了数据,于是查 ...

  7. web项目中js加载慢问题解决思路

    最近使用Echarts地图(版本为echarts2,echarts3目前无法下载地图版). 问题描述:之前使用require形式加载,地图首次加载显示要6-7秒,难以接受. js配置代码如下: < ...

  8. Spring MVC 以.html为后缀名访问获取数据,报406 Not Acceptable错误

    转载,感谢这位博主,有自己的添加. 如题,最近以spring mvc作为后台框架,前端异步获取数据时(.html为后缀名的访问方式),报406 Not Acceptable错误.当初都不知道啥原因,前 ...

  9. jsp页面 列表 展示 ajax异步实现

    1. 服务端先返回页面基本结构(如message.jsp), <%@ page language="java" contentType="text/html; ch ...

随机推荐

  1. ORM框架SQLAlchemy

    SQLAlchemy orm英文全称object relational mapping,就是对象映射关系程序,简单来说就是类似python这种面向对象的程序来说一切皆对象,但是使用的数据库却都是关系型 ...

  2. Linux下部署开源版“禅道”项目管理系统

    1.开源版安装包下载 [root@iZbp ~]# wget http://dl.cnezsoft.com/zentao/9.0.1/ZenTaoPMS.9.0.1.zbox_64.tar.gz 2. ...

  3. EQueue

    EQueue 2.3.2版本发布(支持高可用) - dotNET跨平台 - CSDN博客https://blog.csdn.net/sD7O95O/article/details/78097193 E ...

  4. C#中使用JavaScriptSerializer类实现序列化与反序列化

    1.添加引用 JavaScriptSerializer类的使用需要引用System.Web.Extensions.dll文件,根据路径:C:\Program Files (x86)\Reference ...

  5. vue2.0里的路由钩子

    路由钩子 在某些情况下,当路由跳转前或跳转后.进入.离开某一个路由前.后,需要做某些操作,就可以使用路由钩子来监听路由的变化 全局路由钩子: router.beforeEach((to, from, ...

  6. Flutter之Color

    color:颜色Colors.green ,系统默认了几种颜色,分别如下: red, pink, purple, deepPurple, indigo, blue, lightBlue, cyan, ...

  7. 转 - Linux安装python3.6

    https://www.cnblogs.com/kimyeee/p/7250560.html

  8. Nginx ACCESS阶段 Satisfy 指令

    L:60 这里一定要记住 return 指令所对应的阶段 早与access 因此如果location 有return 的话 那么 deny可能都会失效

  9. Django ORM 操作2 增删改

    增删改 增加 表对象直接增加方式 Frank_obj = models.Student(name ="海东",course="python",birth=&qu ...

  10. C++ bitset 用法

    C++的 bitset 在 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间. 下面是具体用法 构造函数 bitset常用构造函数有四种,如下 bi ...