Servlet3.0特性详解
Servlet3.0 新特性概述
Servlet 3.0 随着 Java EE 6 规范一起发布。Servlet3.0在Servlet2.5的基础上提供了若干新特性,能够简化 Web 应用的开发和部署。以下几个新特性比较值得称赞:
- 异步处理支持
异步处理,使得servlet线程不在需要一直阻塞直到业务处理完成才能输出响应再关闭Servlet线程。异步处理在接收到请求后,Servlet线程可以将耗时的操作委派给另外一个线程来处理,自己在不生成响应的情况下返回至容器。针对业务处理比较耗时的场景,这将大大减少服务器资源的占用并且提高并发处理的速度。 - 新增注解支持
Servlet3.0 新增了若干注解,简化了Servlet、过滤器、监听器的声明。 这使得web.xml部署文件不再是必选。 - 可插性支持
开发者可以通过插件的方式很方便的扩充已有的web应用功能,而不需要修改原有的应用。
异步处理支持
在Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:
- 接收到请求,可能需要对请求携带的数据进行一些预处理;
- 调用业务接口的某些方法,以完成业务处理;
- 根据处理结果返回响应;
- 结束Servlet线程。
在第二步业务处理通常是最为耗时的,业务处理一般包含各种逻辑运算、数据库操作、以及其它跨网络调用等,在此过程中,Servlet一直保持阻塞状态,直到业务方法执行完毕。在整个业务处理过程中,Servlet资源一直被占用,对于高并发的应用,这很有可能造成瓶颈。以前我们通常采用提前kill掉Servlet线程的方案来及时释放资源。
Servlet 3.0 采用异步处理支持之后的流程如下:
- 接收到请求,可能需要对请求携带的数据进行一些预处理;
- Servlet线程将请求转交给一个异步线程来执行业务,线程本身返回容器,此时Servlet还没有生产相应数据,异步线程处理完业务以后可以直接生产响应数据(异步线程拥有ServletRequest和ServletResponse对象的引用)或者将请求转发给其它的Servlet;
异步处理特性可以应用于 Servlet 和过滤器两种组件,由于异步处理的工作模式和普通工作模式在实现上有着本质的区别,因此默认情况下,Servlet 和过滤器并没有开启异步处理特性,如果希望使用该特性,则必须按照如下的方式启用:
web.xml启用
对于使用web.xml文件部署的项目,Servlet3.0在和标签增加了的子标签,该标签默认取值为false。如果需要启用异步处理,那么将这个标签的值设置为true即可。代码示例如下:
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.lfa.servlet.MyServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
注解方式
对于使用@WebServlet 和@WebFilter 注解的的场景,Selvlet3.0 提供了asyncSupported 属性,该属性默认值为false,如要启用异步处理支持,需要将该属性设置为true。代码示例如下:
@WebFilter(urlPatterns = "/test",asyncSupported = true)
public class TestFilter implements Filter{...}
以下是一个简单的模拟异步处理demo
@WebServlet(urlPatterns = "/demo", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
//Servlet 接收服务
out.flush();
//在子线程中执行业务调用,并由其负责输出响应,主线程退出
AsyncContext ctx = req.startAsync();
new Thread(new Executor(ctx)).start();
//Servlet 结束
out.flush();
}
}
//定义一个子线程来处理请求业务
public class Executor implements Runnable {
private AsyncContext ctx = null;
public Executor(AsyncContext ctx){
this.ctx = ctx;
}
public void run(){
try {
//等待十秒钟,以模拟业务方法的执行
Thread.sleep(10000);
PrintWriter out = ctx.getResponse().getWriter();
//业务处理模块
out.flush();
ctx.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Servlet3.0 还提供了一个异步监听器(SyncListenner),它可以监听四种事件:
异步线程开始:
调用AsyncListener的onStartAsync(AsyncEvent event) 方法。
异步线程出错:
调用 AsyncListener 的 onError(AsyncEvent event) 方法。
异步线程执行超时:
调用 AsyncListener 的 onTimeout(AsyncEvent event) 方法。
异步线程执行完毕:
调用 AsyncListener 的 onComplete(AsyncEvent event) 方法。
注册 AsyncListener只需将需要注册的 AsyncListener 对象传递给 AsyncContext 对象的 addListener() 方法即可,如下所示:
AsyncContext ctx = req.startAsync();
ctx.addListener(new AsyncListener() {
public void onComplete(AsyncEvent asyncEvent) throws IOException {
// 做一些清理工作或者其他
}
...
});
新增注解支持
对于Servlet 3.0 的部署描述文件 web.xml ,顶层标签 有一个 metadata-complete 属性,该属性指定当前的部署描述文件是否是完全的。如果设置为 true,则容器在部署时将只依赖部署web.xml描述文件,忽略所有的注解(同时也会跳过 web-fragment.xml 的扫描,即禁用可插性支持);如果不配置该属性,或者将其设置为 false,则表示启用注解支持(和可插性支持)。
@WebServlet
@WebServlet 将一个类声明为 Servlet,在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性(以下所有属性均为可选属性,但是 vlaue 或者 urlPatterns 通常是必需的,且二者不能共存,如果同时指定,通常是忽略 value 的取值):
表 1. @WebServlet 主要属性列表
属性名 | 类型 | 描述 |
---|---|---|
name | String | 制定servlet的name,如果没有指定,默认使用类的名字 |
value | String[] | 等价于urlPatterns,两个属性不能同时公用 |
urlPatterns | String[] | 指定Servlet对url的匹配模式,相当于<url-pattern>标签 |
loadOnStartup | int | 指定Servlet的加载顺序, 相当于 <lonad-on-startup>标签 |
initParams | WebInitParam[] | Servlet初始化参数,相当于<init-param>标签 |
description | String | servlet描述信息,相当于<description>标签 |
displayName | String | servlet显示名称 ,相当于<init-param>标签 |
asyncSupported | boolean | 声明是否支持异步模式,相当于<async-supported>标签 |
举个栗子
@WebServlet(urlPatterns = {"/demo"}, asyncSupported = true,
loadOnStartup = -1, name = "demoServlet", displayName = "demo",
initParams = {@WebInitParam(name = "name", value = "Linfa")}
)
public class SimpleServlet extends HttpServlet{ … }
使用注解方式声明的servlet不需要再web.xml文件中再次声明。
@WebInitParam
为servlet或者filter声明初始化参数,该注解不能单独使用,一般与@Webfilter 或 @WebFilter 一起使用。他的属性如下表所示:
name | String | N | 指定参数的名字,相当于 <param-name> |
value | String | N | 指定参数的值,相当于 <param-value> |
descritption | String | N | 指定参数的值,相当于<description> |
@WebListener
声明该类为一个监听器,需要改声明的对象必须实现以下至少一个接口
ServletContextListener
ServletContextAttributeListener
ServletRequestListener
ServletRequestAttributeListener
HttpSessionListener
HttpSessionAttributeListener
webListener属相如下所示:
属性 | 类型 | 是否必须 | 描述 |
---|---|---|---|
value | String | Y |
举个栗子
@WebListener("这是一个简单的监听")
public class SimpleListener implements ServletContextListener{...}
@MultipartConfig
声明Servlet对文件上传的支持,表示该Sevlet可以处理MIME类型为multipart/form-data的数据。它还提供了一些属性用于简化上传文件请求的处理,具体属性如下表所示:
属性名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
fileSizeThreshold | int | Y | 当数量大于该值是,内容将被写入文件。 |
location | String | Y | 存放上传文件的路径 |
maxFileSize | long | Y | 允许上传文件的大小,默认值是-1,表示没有限制 |
maxRequestSize | long | Y | 针对multipart/form-data请求的最大数量,默认值是-1,表示没有限制 |
@WebFilter
声明一个过滤器,该注解在服务部署时被容器处理。该注解常用的属性如下表所示
属性 | 类别 | 是否必须 | 描述 |
---|---|---|---|
filterName | String | N | 指定过滤器的name,<filter-name> |
value | String[] | N | 相当于<urlPattems >标签,两个不能同时使用 |
urlPatterns | String[] | Y | 指定一组过滤器的url匹配规则 ,相当于<url-pattern>标签 |
servletNames | String[] | N | 指定过滤器将应用于那些servlet,取值对应@WebServletz中的name |
dispatcherTypes | DispatcherType | N | 指定过滤器的转发方式,具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
intiParams | WebInitParam[] | N | 指定一组过滤器的初始化参数,相当于<init-param>标签 |
asyncSupported | boolean | N | 明过滤器是否支持异步操作模式,相当于 标签 |
description | String | N | 相当价于 标签 |
displayName | Stromg | N | 该过滤器的显示名,通常配合工具使用,相当于 标签 |
举个栗子
@WebFilter(servletNames = {"SimpleServlet"},filterName="SimpleFilter")
public class LessThanSixFilter implements Filter{...}
可插性支持
Servlet3.0 可以再不修改已有的web应用的条件下,实现web应用新功能的扩充。实现方式是按照一定的规则打包成jar包,并将jar包放到 WEB-INF/lib目录下即可。
ServletContext性能增强
ServletContext对象在Servlet3.0得到了增强。现在该对象支持运行时动态部署Servlet、过滤器、监听器,以及为servlet和过滤器增加url映射配置。
HttpServletRequest 对文件上传的支持
在Servlet3.0之前,实现一个上传文件的servlet是一件非常头疼的事情。因为servlet本身并不提供这一支持,需要依赖第三方框架来实现,使用起来不是很方便。Servlet3.0 已经提供了文件上传的支持,使用方式相当简单。HttpServletRequest中提供了两个操作文件上传参数的方法:
Part getPart(String name) :获取给定name的文件
Collection <Part> getParts() :获取全部上传的文件
每一个文件用一个Part对象来表示,该接口提供了几个简单的文件处理方法,如 write() 和 delete() 等。
举个栗子
request.getPart("photo").write("/mnt/images/photo.jpg")
我们只用一行代码就实现了上传文件的保存功能,是不是棒棒的?
总结
Servlet3.0 的出现使的web开发变得更简单。