22,请求封装器
对于容器产生的HttpServletRequest对象,无法直接修改某些信息,如请求参数值就是一个例子。如果实现HttpServletRequest接口,让getParameter()返回
过滤后的请求参数值,HttpServletRequest接口定义的方法都要实现,这相当麻烦。
有个HttpServletRequestWraper实现了HttpServletRequest接口,只要继承HttpServletRequestWrapper类,并编写想要重新定义的方法即可。相对应于ServletRequest接口,
也有个ServletRequestWerapper类可以使用.
23,响应封装器
要实现响应封装器可以继承HttpServletResponseWrapper类对ttpServletResponse对象进行封装。
24,异步处理
Web容器会为每个请求分配一个线程,默认情况下,响应完成前,该线程占用的资源都不会被释放。若有些请求需要长时间处理,
就会长时间占用线程所需资源,若此类请求很多,许多线程资源都被长时间占用,会对系统的性能造成负担。
Servlet3.0新增了异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在
处理完成时在对客户端进行响应。
为了支持异步处理,在Servlet3.0中,在ServletRequest上提供了startAsync()方法:
AsyncContext startAsync() throws java.lang.IllegalStateException;
AsyncContext startAsync(ServletRequest servletRequest,ServletResponse servletResponse) throws java.lang.IllegalStateException;
这两个方法都会返回AsyncContext接口的实现对象,前者会直接利用原有的请求与响应对象来创建AsyncContext,后者可以传入自行创建的请求,响应封装对象。
在调用了startAsync()方法取得AsyncContext对象之后,此次请求的响应会被延后,并释放容器分配的线程。
可以通过AsyncContext的getRequest(),getResponse()方法取得请求,响应对象,此次对客户端的响应将暂缓至调用AsyncContext的complete()或dispatch()方法为止,
前者表示响应完成,后者表示将调派指定的URL进行响应。
若能调用ServletRequest的startAsync()以取得AsyncContext,必须告知容器此Servlet支持异步处理,如果使用@WebServlet来标注,则可以设置asyncSupported为true。
如果Servlet将会进行异步处理,若其前端有过滤器,则过滤器亦需标示其支持异步处理,如果使用@WebFilter,同样可以设置其asyncSupported为true。
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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="AsyncServlet",urlPatterns={"/async.do"},
asyncSupported=true)//告诉容器此Servlet支持异步处理
public class AsyncServlet extends HttpServlet{
private ExecutorService executorService=Executors.newFixedThreadPool(10);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
resp.setContentType("text/html; charset=UTF8");
AsyncContext ctx=req.startAsync();//开始异步处理,释放请求线程
executorService.submit(new AsyncRequest(ctx));//创建AsyncRequest,调度线程
}
@Override
public void destroy() {
// TODO Auto-generated method stub
super.destroy();
executorService.shutdown();
}
}
首先告诉容器,这个Servlet支持异步处理,对于每个请求,Servlet会取得其AsyncContext,并释放容器所分配的线程,响应被延后。对于这些被延后响应的请求,
创建一个实现Runnable接口的AsyncRequest对象,并将其调度一个线程池(Threadpool),线程池的线程数量是固定的,让这些必须长时间处理的请求,在这些有限数量
的线程中完成,而不用每次请求都占用容器分配的线程。
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
public class AsyncRequest implements Runnable{
private AsyncContext ctx;
public AsyncRequest(AsyncContext ctx){
this.ctx=ctx;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(10000);
PrintWriter out=ctx.getResponse().getWriter();
ctx.complete();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}
}
25,更多AsyncContext细节
如果Servlet或过滤器的asyncSupported被标示为True,则它们支持异步请求处理,在不支持异步处理的Servlet或过滤器中调用startAsync(),会抛出
IllegalStateException。
当在支持异步处理的Servlet或过滤器中调用请求对象的startAsync()方法时,该次请求会离开容器所分配的线程,这意味着必须响应处理流程会返回,也就是
若有过滤器,也会依序返回(也就是完成各自FilterChain的doFilter()方法),但最终的响应被延迟。
可以调用AsyncContext的complete()方法完成响应,而后调用forward()方法,将相应转发给别的Servlet/JSP处理,AsyncContext的forward()将请求的响应权派送给别的
页面来处理,给定的路径时相对于ServletContext的路径。不可以自行在同一个AsyncContext上同时调用complete()与forward(0,否则会抛出IllegalStateException。
不可以在两个异步处理的Servlet间派送前,连续调用两次startAsync(),否则会抛出IllegalStateException。
将请求从支持异步处理的Servlet派送至一个同步处理Servlet是可行的,此时,容器会负责调用AsyncContext的complete()。
如果从一个同步处理的Servlet派送请求至一个支持异步处理的Servlet,在异步处理的Servlet中调用startAsync,会抛出IllegalStateException。
若对AsyncContext的起始,完成,超时或错误发生等事件有兴趣,可以实现AsyncListener。其定义如下:
package javax.servlet;
// Compiled from AsyncListener.java (version 1.7 : 51.0, no super bit)
public abstract interface javax.servlet.AsyncListener extends java.util.EventListener {
// Method descriptor #5 (Ljavax/servlet/AsyncEvent;)V
public abstract void onComplete(javax.servlet.AsyncEvent arg0) throws java.io.IOException;
// Method descriptor #5 (Ljavax/servlet/AsyncEvent;)V
public abstract void onTimeout(javax.servlet.AsyncEvent arg0) throws java.io.IOException;
// Method descriptor #5 (Ljavax/servlet/AsyncEvent;)V
public abstract void onError(javax.servlet.AsyncEvent arg0) throws java.io.IOException;
// Method descriptor #5 (Ljavax/servlet/AsyncEvent;)V
public abstract void onStartAsync(javax.servlet.AsyncEvent arg0) throws java.io.IOException;
}
AsyncContext有个addListener()方法,可以加入AsyncListener的实现对象,在对应事件发生时会调用AsyncListener实现对象的对应方法。
如果调用AsynccContext()的dispatch(),将请求调派给别的Servlet,则可以通过请求对象的getAttribute()取得以下属性:
javax.servlet.async.request_uri
javax.servlet.async.context_path
javax.servlet.async.servlet_path
javax.servlet.async.path_info
javax.servlet.async.query_string
其值分别等于调用HttpServletReuqest的getRequestURI(),getContextPath(),getServletPath(),getPathInfo()与getQueryString()所取得结果。