Servlet3.0新特性使用详解



Servlet3.0新特性使用详解 .
在infoq上有关于servlet3.0的新特性说明,个人觉得比较全面

•可插拔的Web框架
◦几乎所有基于Java的web框架都建立在servlet之上。现今大多数web框架要么通过servlet、要么通过Web.xml插入。利用标注(Annotation)来定义servlet、listener、filter将使之(可插拔)成为可能。程序访问web.xml和动态改变web应用配置是所期望的特性。该JSR将致力于提供把不同web框架无缝地插入到web应用的能力。
•EOD
◦标注——利用标注来作为编程的声明风格。
◦web应用零配置是EoD努力方向之一。部署描述符将被用来覆盖配置。
◦范型(generic)——在API中尽可能利用范型。
◦使用其它语言增强可能需要改善API可用性的地方。
•支持异步和Comet
◦非阻塞输入——从客户端接收数据,即使数据到达缓慢也不会发生阻塞。
◦非阻塞输出——发送数据到客户端,即使客户端或网络很慢也不会发生阻塞。
◦延迟请求处理——Ajax web应用的Comet风格,可以要求一个请求处理被延迟,直到超时或一个事件发生。延迟请求处理对以下情况也很有用:如果远程的/迟缓的资源必须在为该请求服务之前被获得;或者如果访问一个特殊资源,其需要扼杀一些请求以防止太多的并发访问。
◦延迟响应关闭——Ajax web应用的Comet风格,可以要求响应保持打开,以允许当异步事件产生时发送额外的数据。
◦阻塞/非阻塞通知——通知阻塞或非阻塞事件。
◦频道概念——订阅一个频道,以及从该频道获取异步事件的能力。这意味着可以创建、订阅、退订,以及应用一些诸如谁能加入、谁不能加入的安全限制。
•安全
◦login/logout能力。
◦自注册。
•结合
◦结合/需求,来自REST JST JSR(JSR 311 )。
◦结合/需求,来自JSF 2.0 JSR(JSR 134 )。
•其它
◦支持更好的欢迎文件(welcome file)。
◦ServletContextListener排序。
◦容器范围内定义init参数。
◦文件上载——过程侦听——存储中间或最终文件。
◦澄清线程安全问题。

我们下面就看看其中几个特性:

1.可插拔的Web框架,其实就是web.xml中可以又多个子模块的配置文件组成,而各个子模块的配置文件可以放在各个jar包的META-INFO中,这样就实现web应用的模块化。

类似,可以按照配置的顺序指定了web片段的顺序。通过absolute-ordering进行绝对顺序配置,通过每个fragment的order的after和before标签进行相对顺序配置。

[java] view plaincopyprint?
01.
02. 03. xmlns="http://java.sun.com/xml/ns/javaee"
04. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
05. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
06. http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
07.
08. web-fragment1
09. web-fragment2
10.


xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

web-fragment1
web-fragment2

每个fragment1的配置如下:

[html] view plaincopyprint?
01.
02. 03. xmlns="http://java.sun.com/xml/ns/javaee"
04. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
05. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
06. http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">
07.
08. web-fragment1
09. web-fragment1
10.
11.


xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">

web-fragment1
web-fragment1

2. servlet3.0的annotation支持

对于原来在web.xml定义的servlet,filter,listener,InitParam都可以通过annotation来配置了,而不需要在web.xml中定义。

@WebFilter

[html] view plaincopyprint?
01.import java.io.IOException;
02.
03.import javax.servlet.Filter;
04.import javax.servlet.FilterChain;
05.import javax.servlet.FilterConfig;
06.import javax.servlet.ServletException;
07.import javax.servlet.ServletRequest;
08.import javax.servlet.ServletResponse;
09.import javax.servlet.annotation.WebFilter;
10.import javax.servlet.annotation.WebInitParam;
11.
12.//asyncSupported=true 对应filter也需要定义asyncSupported=true
13.@WebFilter(urlPatterns={“/*”}, filterName=”my3Filter”, asyncSupported=true)
14.@WebInitParam(name=”a”, value=”valuea”)
15.public class My3Filter implements Filter{
16.
17. @Override
18. public void destroy() {
19. // TODO Auto-generated method stub
20.
21. }
22.
23. @Override
24. public void doFilter(ServletRequest arg0, ServletResponse arg1,
25. FilterChain arg2) throws IOException, ServletException {
26. System.out.println(“servlet 3 filter”);
27. arg2.doFilter(arg0, arg1);
28. }
29.
30. @Override
31. public void init(FilterConfig arg0) throws ServletException {
32. System.out.println(“servlet 3 filter init”);
33. }
34.
35.}
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;

//asyncSupported=true 对应filter也需要定义asyncSupported=true
@WebFilter(urlPatterns={“/*”}, filterName=”my3Filter”, asyncSupported=true)
@WebInitParam(name=”a”, value=”valuea”)
public class My3Filter implements Filter{

@Override
public void destroy() {
// TODO Auto-generated method stub

}

@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
System.out.println(“servlet 3 filter”);
arg2.doFilter(arg0, arg1);
}

@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println(“servlet 3 filter init”);
}

}

@WebServlet

[html] view plaincopyprint?
01.import java.io.IOException;
02.
03.import javax.servlet.Filter;
04.import javax.servlet.FilterChain;
05.import javax.servlet.FilterConfig;
06.import javax.servlet.ServletException;
07.import javax.servlet.ServletRequest;
08.import javax.servlet.ServletResponse;
09.import javax.servlet.annotation.WebFilter;
10.import javax.servlet.annotation.WebInitParam;
11.
12.//asyncSupported=true 对应filter也需要定义asyncSupported=true
13.@WebFilter(urlPatterns={“/*”}, filterName=”my3Filter”, asyncSupported=true)
14.@WebInitParam(name=”a”, value=”valuea”)
15.public class My3Filter implements Filter{
16.
17. @Override
18. public void destroy() {
19. // TODO Auto-generated method stub
20.
21. }
22.
23. @Override
24. public void doFilter(ServletRequest arg0, ServletResponse arg1,
25. FilterChain arg2) throws IOException, ServletException {
26. System.out.println(“servlet 3 filter”);
27. arg2.doFilter(arg0, arg1);
28. }
29.
30. @Override
31. public void init(FilterConfig arg0) throws ServletException {
32. System.out.println(“servlet 3 filter init”);
33. }
34.
35.}
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;

//asyncSupported=true 对应filter也需要定义asyncSupported=true
@WebFilter(urlPatterns={“/*”}, filterName=”my3Filter”, asyncSupported=true)
@WebInitParam(name=”a”, value=”valuea”)
public class My3Filter implements Filter{

@Override
public void destroy() {
// TODO Auto-generated method stub

}

@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
System.out.println(“servlet 3 filter”);
arg2.doFilter(arg0, arg1);
}

@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println(“servlet 3 filter init”);
}

}

支持的annotation如下,都可以通过eclipse的提示查到对应的参数配置,

@WebServlet支持的参数有

3. servlet3.0的异步支持

很多时候Servlet要和其他的资源进行互动,例如访问数据库,调用web service。在和这些资源互动的时候,Servlet不得不等待数据返回,然后才能够继续执行。这使得Servlet调用这些资源的时候阻塞。Servlet3.0通过引入异步处理解决了这个问题。异步处理允许线程调用资源的时候不被阻塞,而是直接返回。AsyncContext负责管理从资源来的回应。AsyncContext决定该回应是应该被原来的线程处理还是应该分发给容器中其他的资源。AsyncContext有一些方法如start,dispatch和complete来执行异步处理。
要想使用Servlet3.0的异步处理,我们需要设置@Webservlet和@WebFilter注解的asyncSupport属性。这个属性是布尔值,缺省值是false。

所以,可以在servlet阻塞处理网络,数据库查询等时,可以暂时释放线程资源,处理更多请求,当请求处理完之后重新唤醒线程继续处理原来的请求,达到NIO的效果。


我们看下一个示例

[java] view plaincopyprint?
01.AsyncContext 可以添加监听器作为异步处理过程中状态的跟踪等
AsyncContext 可以添加监听器作为异步处理过程中状态的跟踪等

[java] view plaincopyprint?
01.import java.io.IOException;
02.import java.util.Date;
03.
04.import javax.servlet.AsyncContext;
05.import javax.servlet.AsyncEvent;
06.import javax.servlet.AsyncListener;
07.import javax.servlet.ServletConfig;
08.import javax.servlet.ServletException;
09.import javax.servlet.ServletRequest;
10.import javax.servlet.annotation.WebServlet;
11.import javax.servlet.http.HttpServlet;
12.import javax.servlet.http.HttpServletRequest;
13.import javax.servlet.http.HttpServletResponse;
14.
15.@WebServlet(asyncSupported=true,name=”asyncServlet”, urlPatterns=”/async”)
16.public class AsyncServlet extends HttpServlet{
17.
18. /**
19. *
20. */
21. private static final long serialVersionUID = 3903580630389463919L;
22.
23. @Override
24. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
25. throws ServletException, IOException {
26. resp.getWriter().write(“hello, async test”);
27. resp.getWriter().println(“start:”+new Date()+”.
“);
28. resp.getWriter().flush();
29. final AsyncContext async = req.startAsync(req,resp);
30. async.setTimeout(3000);
31. async.start(new Runnable() {
32. @Override
33. public void run() {
34. ServletRequest request = async.getRequest();
35. try {
36. Thread.sleep(2000);
37. async.getResponse().getWriter().write(“aync thread processing”);
38. async.getResponse().getWriter().flush();
39. async.getResponse().getWriter().println(“async end:”+new Date()+”.
“);
40. async.getResponse().getWriter().flush();
41. } catch (InterruptedException e) {
42. // TODO Auto-generated catch block
43. e.printStackTrace();
44. } catch (IOException e) {
45. // TODO Auto-generated catch block
46. e.printStackTrace();
47. }
48. }
49. });
50. async.addListener(new AsyncListener() {
51.
52. @Override
53. public void onTimeout(AsyncEvent arg0) throws IOException {
54. // TODO Auto-generated method stub
55.
56. }
57.
58. @Override
59. public void onStartAsync(AsyncEvent arg0) throws IOException {
60. // TODO Auto-generated method stub
61.
62. }
63.
64. @Override
65. public void onError(AsyncEvent arg0) throws IOException {
66. // TODO Auto-generated method stub
67.
68. }
69.
70. @Override
71. public void onComplete(AsyncEvent arg0) throws IOException {
72. // TODO Auto-generated method stub
73.
74. }
75. });
76. resp.getWriter().println(“end:”+new Date()+”.
“);
77. resp.getWriter().flush();
78.
79. }
80.
81.}
import java.io.IOException;
import java.util.Date;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(asyncSupported=true,name=”asyncServlet”, urlPatterns=”/async”)
public class AsyncServlet extends HttpServlet{

/**
*
*/
private static final long serialVersionUID = 3903580630389463919L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write(“hello, async test”);
resp.getWriter().println(“start:”+new Date()+”.
“);
resp.getWriter().flush();
final AsyncContext async = req.startAsync(req,resp);
async.setTimeout(3000);
async.start(new Runnable() {
@Override
public void run() {
ServletRequest request = async.getRequest();
try {
Thread.sleep(2000);
async.getResponse().getWriter().write(“aync thread processing”);
async.getResponse().getWriter().flush();
async.getResponse().getWriter().println(“async end:”+new Date()+”.
“);
async.getResponse().getWriter().flush();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
async.addListener(new AsyncListener() {

@Override
public void onTimeout(AsyncEvent arg0) throws IOException {
// TODO Auto-generated method stub

}

@Override
public void onStartAsync(AsyncEvent arg0) throws IOException {
// TODO Auto-generated method stub

}

@Override
public void onError(AsyncEvent arg0) throws IOException {
// TODO Auto-generated method stub

}

@Override
public void onComplete(AsyncEvent arg0) throws IOException {
// TODO Auto-generated method stub

}
});
resp.getWriter().println(“end:”+new Date()+”.
“);
resp.getWriter().flush();

}

}
输出如下: 这里先对线程后面的println先输出,最后在处理输出异步线程输出的内容。

[html] view plaincopyprint?
01.hello, async teststart?Mon Dec 10 20:23:35 CST 2012.
02.end?Mon Dec 10 20:23:35 CST 2012.
03.aync thread processingasync end?Mon Dec 10 20:23:37 CST 2012.
hello, async teststart?Mon Dec 10 20:23:35 CST 2012.
end?Mon Dec 10 20:23:35 CST 2012.
aync thread processingasync end?Mon Dec 10 20:23:37 CST 2012.
这里有个主意点,对于servlet配置了asyncSupported=true,那么对于所有异步经过的filter也需要配置这个参数,否则这里会报错,不支持异步处理。

4.@MultipartConfig 文件上传的支持,以前servlet要处理上传文件一般会使用common file upload组件,现在servlet3.0原生支持了文件上传的处理

location参数指定临时文件存放目录,’

[java] view plaincopyprint?
01.package com.servlet;
02.
03.import java.io.IOException;
04.
05.import javax.servlet.ServletException;
06.import javax.servlet.annotation.MultipartConfig;
07.import javax.servlet.annotation.WebServlet;
08.import javax.servlet.http.HttpServlet;
09.import javax.servlet.http.HttpServletRequest;
10.import javax.servlet.http.HttpServletResponse;
11.import javax.servlet.http.Part;
12.
13.
14.@WebServlet(asyncSupported=true,name=”upload”, urlPatterns=”/upload”)
15.@MultipartConfig(fileSizeThreshold = 10000, maxFileSize = 1000000, maxRequestSize = 1000000, location=”E:/logs”)
16.public class MultiPartServlet3 extends HttpServlet {
17.
18. /**
19. *
20. */
21. private static final long serialVersionUID = 7306582588845300635L;
22.
23. @Override
24. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
25. throws ServletException, IOException {
26. Part part = req.getPart(“file”);
27. String value = part.getHeader(“content-disposition”);
28. System.out.println(value);
29. String filename = value.substring(value.lastIndexOf(“=”) + 2,value.length() – 1);
30. System.out.println(filename);
31. System.out.println(part.getInputStream().toString());
32. }
33.
34.}
package com.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@WebServlet(asyncSupported=true,name=”upload”, urlPatterns=”/upload”)
@MultipartConfig(fileSizeThreshold = 10000, maxFileSize = 1000000, maxRequestSize = 1000000, location=”E:/logs”)
public class MultiPartServlet3 extends HttpServlet {

/**
*
*/
private static final long serialVersionUID = 7306582588845300635L;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Part part = req.getPart(“file”);
String value = part.getHeader(“content-disposition”);
System.out.println(value);
String filename = value.substring(value.lastIndexOf(“=”) + 2,value.length() – 1);
System.out.println(filename);
System.out.println(part.getInputStream().toString());
}

}

我们写一个文件上传的页面

[html] view plaincopyprint?
01.

02.
03. value="submit">
04.

value="submit">

随便上传一个文件,我这边的输出为:

[html] view plaincopyprint?
01.form-data; name=”file”; filename=”22.log”
02.22.log
03.java.io.ByteArrayInputStream@1bdce67
form-data; name=”file”; filename=”22.log”
22.log
java.io.ByteArrayInputStream@1bdce67
5.已有API改进,特别是支持动态加载servlet,热部署功能

[java] view plaincopyprint?
01.HttpServletRequest
02.To support the multipart/form-data MIME type, the following methods have been added to the HttpServletRequest interface:
03.为了支持multipart/form-data MIME类型,在HttpServletRequest接口中添加了项目的方法:
04. * Iterable getParts()
05. * Part getPart(String name)
06.Cookies
07.为了避免一些跨站点攻击,Servlet3.0支持HttpOnly的cookie。HttpOnly cookie不想客户端暴露script代码。Servlet3.0在Cookie类中添加了如下的方法来支持HttpOnly cookie:
08. * void setHttpOnly(boolean isHttpOnly)
09. * boolean isHttpOnly()
10.ServletContext
11.通过在ServletContext中添加下面的方法,Servlet3.0允许Servlet或filter被编程的加入到context中:
12. * addServlet(String servletName, String className)
13. * addServlet(String servletName, Servlet servlet)
14. * addServlet(String servletName, Class servletClass)
15. * addFilter(String filterName, String className)
16. * addFilter(String filterName, Filter filter)
17. * addFilter(String filterName, ClassfilterClass)
18. * setInitParameter (String name, String Value)
HttpServletRequest
To support the multipart/form-data MIME type, the following methods have been added to the HttpServletRequest interface:
为了支持multipart/form-data MIME类型,在HttpServletRequest接口中添加了项目的方法:
* Iterable getParts()
* Part getPart(String name)
Cookies
为了避免一些跨站点攻击,Servlet3.0支持HttpOnly的cookie。HttpOnly cookie不想客户端暴露script代码。Servlet3.0在Cookie类中添加了如下的方法来支持HttpOnly cookie:
* void setHttpOnly(boolean isHttpOnly)
* boolean isHttpOnly()
ServletContext
通过在ServletContext中添加下面的方法,Servlet3.0允许Servlet或filter被编程的加入到context中:
* addServlet(String servletName, String className)
* addServlet(String servletName, Servlet servlet)
* addServlet(String servletName, Class servletClass)
* addFilter(String filterName, String className)
* addFilter(String filterName, Filter filter)
* addFilter(String filterName, ClassfilterClass)
* setInitParameter (String name, String Value)
下面这篇文章还是比较全面的解析

http://www.ibm.com/developerworks/cn/java/j-lo-servlet30/index.html

http://www.infoq.com/cn/news/2007/06/servlet3