jsp 异步处理
一. 概述
异步处理功能可以节约容器线程。你应该将此功能 使用在长时间运行的操作上。此功能的作用是释放正在 等待完成的线程,使该线程能够被另一请求所使用。
二. 编写异步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 异步处理的更多相关文章
- ajax 异步 通信 小例子 servlet与 jsp异步 post方法
post请求 url后面加参数 接收不到的,必须 放到send("use"=user)形式 还要加上 xhr.setRequestHeader("Content-Type ...
- ajax 异步 通信 小例子 servlet与 jsp异步 get
get 请求参数通过 url那里写进去,然后send(null) html文件和 servlet进行通信 通过ajax 进行通信 <!DOCTYPE html PUBLIC "-// ...
- Filter 快速开始 异步Servlet 异步请求 AsyncContext 异步线程 异步派发 过滤器拦截
[web.xml] <filter> <filter-name>normalFilter</filter-name> <filter-class>net ...
- url 处理
一.jsp异步请求后台(servlet) 的url RegisterServlet 与 web.xml 的路径一样 function checkPhoneNumber(){ var phonenum ...
- Spring MVC(二)
spring mvc工作流 1A)客户端发出http请求,只要请求形式符合web.xml 文件中配置的*.action的话,就由DispatcherServlet 来处理. 1B)Dispatcher ...
- Spring MVC 以.html为后缀名访问获取数据,报406 Not Acceptable错误。
如题,最近以spring mvc作为后台框架,前端异步获取数据时(.html为后缀名的访问方式),报406 Not Acceptable错误.当初都不知道啥原因,前后台都没报错就是返回不了数据,于是查 ...
- web项目中js加载慢问题解决思路
最近使用Echarts地图(版本为echarts2,echarts3目前无法下载地图版). 问题描述:之前使用require形式加载,地图首次加载显示要6-7秒,难以接受. js配置代码如下: < ...
- Spring MVC 以.html为后缀名访问获取数据,报406 Not Acceptable错误
转载,感谢这位博主,有自己的添加. 如题,最近以spring mvc作为后台框架,前端异步获取数据时(.html为后缀名的访问方式),报406 Not Acceptable错误.当初都不知道啥原因,前 ...
- jsp页面 列表 展示 ajax异步实现
1. 服务端先返回页面基本结构(如message.jsp), <%@ page language="java" contentType="text/html; ch ...
随机推荐
- js中的枚举
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的.可枚举性决定了这个属性能否被for…in查找遍历到. js中基本包装类型的原型属性是不可枚举的 ...
- Powershell同时使用可选强制参数
支持所有PS版本 在下面脚本函数中让可选参数和强制参数必须同时使用. 下面演示当可选参数出现,也必须使用这个强制参数. function Connect-Somewhere { [CmdletBind ...
- springboot 出现 Connection refused: connect
总结网上的方法再结合我自己的方法 1. springCloud com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectEx ...
- Shiro限制登录尝试次数
/** * 认证信息.(身份验证) : Authentication 是用来验证用户身份 * * @param token * @return * @throws AuthenticationExce ...
- Python之路1-变量、数据类型、循环语法
1.python语言介绍 编程语言主要从以下几个角度进行分类,编译型和解释型,静态语言和动态语言,强类型定义语言和弱类型定义语言. 编译和解释区别 编译器是把源程序的每一条语句都编译成机器语言,并保存 ...
- 基于maven的spring-boot的pom文件详解
Spring Boot 推荐的基础 POM 文件 名称 说明 spring-boot-starter 核心 POM,包含自动配置支持.日志库和对 YAML 配置文件的支持. spring-boot-s ...
- springcloud 新增微服务
个人记录 记录公司微服务项目,模块添加的步骤 一 创建Module 选择maven groupid和artifactid 参考 pom文件 <project xmlns="http: ...
- SQL 中左连接与右链接的区别
在微信公众号中看到的sql左连接与右链接的总结,这个图总结的很好,所以单独收藏下:
- 洛谷P3806 点分治
点分治 第一次写点分治..感觉是一个神奇而又暴力的东西orz 点分治大概就是用来处理树上链的信息,把路径分成过点x和不过点x的两种,不过点x的路径可以变成过点x的子树中一点的路径,递归处理 #incl ...
- 2.4 random 模块