jsp 异步处理

时间:2022-09-07 00:05:40

一.  概述

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

二. 编写异步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:

jsp 异步处理

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

  除了调度到其他资源去完成任务,你也可以调用 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>

jsp 异步处理

四. 异步监听器

  为支持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 异步处理

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 以&period;html为后缀名访问获取数据,报406 Not Acceptable错误。

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

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

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

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

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

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

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

随机推荐

  1. [Python基础知识]正则

    import re str4 = r"^http://qy.chinahr.com/cvm/preview\?cvid=\w{24,25}&from=sou&gtid=\w{ ...

  2. Android中ListView控件的使用

    Android中ListView控件的使用 ListView展示数据的原理 在Android中,其实ListView就相当于web中的jsp,Adapter是适配器,它就相当于web中的Servlet ...

  3. 【转】c&plus;&plus;中引用的全方位解读

    对于习惯使用C进行开发的朋友们,在看到c++中出现的&符号,可能会犯迷糊,因为在C语言中这个符号表示了取地址符,但是在C++中它却有着不同的用途,掌握C++的&符号,是提高代码执行效率 ...

  4. Filter案例

    1.有选择的被访问 描述:首先若用户没有在页面提交注册(直接访问list.jsp),就只能被允许访问a.jsp.其他页面均不被允许访问 在login.jsp提交信息之后,可以在b.jsp访问, 代码如 ...

  5. corejava&lowbar;chap02

    //单行注释  --不能用在一行代码的中间/**/多行注释 --任何地方/** */文档注释  文档注释用在:package.class.member variables.member method. ...

  6. 类和对象:拾遗 - 零基础入门学习Python039

    类和对象:拾遗 让编程改变世界 Change the world by program 这节课谈的内容主要有: 组合 ...... 此处省略N多内容,具体请看视频讲解 ...... 类.类对象和实例对 ...

  7. g&plus;&plus; error&colon; expected nested-name-specifier before &&num;39&semi;XXX&&num;39&semi;

    template <typename addrT=int,typename valuT=int,typename stream_addrT=bm_addr,typename stream_siz ...

  8. 爬虫高性能 asyncio库 twisted库 tornado库

    一 背景知识 爬虫的本质就是一个socket客户端与服务端的通信过程,如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,那只能等待爬取一个结束后才能继续下一个,效率会非常低. 需要强调的是 ...

  9. 【iOS开发-56】案例BUG:button的enabled、控件的userInteractionEnabled以及两种提示框UIAlert和UIActionSheet

    接上述案例找BUG:[iOS开发-51]案例学习:动画新写法.删除子视图.视图顺序.延迟方法.button多功能使用方法及icon图标和启动页设置 (1)BUG:答案满了就不能再点击optionbut ...

  10. python 之操作mysql 数据库实例

    对于python操作mysql 数据库,具体的步骤应为: 1. 连接上mysql host 端口号 数据库 账号 密码2. 建立游标3. 执行sql(注意,如果是update,insert,delet ...