Posts tagged Servlet
Servlet 3.0 请求异步处理
0一、什么是请求异步处理
请求异步处理是Servlet 3.0规范中引入的一个新的东西,因为有时候,我们请求的资源需要一段时间才能够返回(比如从网络或者数据库取大量数据),这个时候处理请求的线程就处于忙等待的状态,没有办法去处理其他的请求,造成了资源上的浪费,所以在Servlet 3.0规范中引入了请求的异步处理,让一些耗时比较长的事情交给异步线程处理,将容器的处理线程给释放出来,提高资源的利用率。
二、如何让一个Servlet或者Filter支持请求异步处理
在Servlet 3.0中,你可以在Servlet或者Filter中使用请求异步处理,这里面仅仅以Servlet为例,在Filter中使用请求异步处理和在Servlet中使用请求异步处理是相同的。
要让一个Servlet支持请求异步处理,你可以在web.xml中定义servlet的时候,再添加一个<async-support>标签,值设为true,样例如下:
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>com.khotyn.test.servlet.HelloServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
当然,由于Servlet 3.0中支持以注解的方式声明Servlet,你还可以在注解中将一个Servlet标明为支持请求异步处理:
@WebServlet(urlPatterns = "/hello", asyncSupported = true)
public class HelloServlet extends HttpServlet {
….........
}
三、如何使用请求异步处理
在将一个Servlet表明为支持请求异步处理以后,你就可以在Servlet中使用AsyncContext了,我们看一下样例代码:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
final AsyncContext context = request.startAsync(request, response);
context.start(new Runnable() {
@Override
public void run() {
System.out.println("Hello, world!");
context.complete();
}
});
}
这是一个简单的例子,里面有一个关键的类,AsyncContext,从这个例子中我们可以看到AsyncContext的基本使用流程:
首先,通过Request的startAsync方法获取AsyncContext,其中startAsync方法有两种形式:
public AsyncContext startAsync() throws IllegalStateException; public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException;
其中,第一种不带参数的直接采用Servlet容器传过来的Request和Response,而第二个可以是包装过的Request和Response,如果你的Servlet没有标明支持请求的异步处理,那么调用这个方法会抛出IllegalStateException。
然后,我们调用了AsyncContext的start方法来启动异步线程,而在异步线程的run方法里面,调用了context的complete()方法来完成异步线程的调用,在不支持请求异步处理的Servlet中,请求等到Servlet的service()方法退出后就被提交了,而在支持AsyncContext的Servlet中,需要等到异步线程调用了AsyncContext的complete()才会被提交。
另外有一点需要说明的是,你不一定要采用AsyncContext的start方法来启动异步线程,事实上,你可以采用任何一种方式启动异步线程,Thread.start(),executor.execute()方法等等都可以启动,Servlet容器关心的是AsyncContext的状态,至于你用什么方式启动线程,启动了多少的线程去做。
怎么样?AsyncContext使用起来非常简单吧!
一个ServletContainerInitializer的例子
0最近在看Servlet规范3.0,里面有一个接口叫做ServletContainerInitializer,网上关于这个接口使用方法的资料比较少,故而写一篇出来记录一下ServletContainerInitializer的使用方法。
一、什么是ServletContainerInitializer
ServletContainerInitializer是Servlet规范3.0提供的一个新的接口,它有一个方法:
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
提供这个接口的目的是为了让Servlet的编程人员可以动态的加载,或者修改Servlet,Filter和Listener。
二:如何使用ServletContainerInitializer
既然ServletContainerInitializer是一个接口,那么首先,你当然要为这个接口提供一个实现类,那么Servlet容器是如何加载到这个实现类的呢,按照Servlet规范3.0,你需要WEB-INF的lib包下面有一个jar文件,这个jar文件包含了一个META-INF/services/javax.servlet.ServletContainerInitializer文件,在这个文件里面你需要将你的实现类的全限定类名添加到里面。
另外,你需要一个为你的实现类添加一个HandlesTypes注解,这个注解有一个value值,这个值是一个Class数组,包含了你想要处理的类,容器会扫描出所有匹配这个数组里面的类的类(包含实现,扩展或者被注解的类),然后将这个类所有onStartup的第一个参数传入,然后你就可以处理了
下面我们看一个ServletContainerInitializer的实现类:
/**
* 测试ServletContainerInitializer
*
* @author khotyn 2011-10-8 下午2:54:09
*/
@HandlesTypes(value = { HttpServlet.class })
public class HelloInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
System.out.println("Hello, Initializer!");
for (Class<?> clazz : c) {
if (clazz.getName().contains("ByteServlet")) {
Dynamic dynamic = ctx.addServlet("byte", clazz.getName());
dynamic.addMapping("/byte");
}
}
}
}
容器会扫描出所有实现了HttpServlet这个类的类,如果类名包含了ByteServlet,就动态地加载到容器中去,并将路径映射到”/byte”下。