@随笔 Servlet3.0新特性
夜光序言:
苹果落地,昼夜更替,我爱上你,是世界自然规律。
正文:
1 Servlet3.0新特性概述
Servlete3.0的主要新特性如下三部分:
- 使用@WebServlet、@WebFilter、@WebListener三个注解来替代web.xml文件中的Servlet、Filter、Listener的配置;
- Servlet异步处理:当Servlet处理比较费时的问题时,这会让客户感觉到很卡。当使用异常处理时可以把已经处理好的内容先一步响应给客户端浏览器,然后使用另一个线程来完成费时的操作,也就是把内容一部分一部分的显示出来;
- 上传组件:不用再使用fileupload等第三方的上传组件,使用Servlet3.0的上传组件会更方便。
2 @WebServlet、@WebFilter、@WebListener
@WebServlet( urlPatterns={"/AServlet"}, initParams={@WebInitParam(name="paramName",value="paramValue")}, loadOnStartup=1 ) public class AServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { System.out.println(config.getInitParameter("paramName")); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); response.getWriter().print("Genius~~~~"); } } |
@WebFilter(urlPatterns={"/*"}, dispatcherTypes={DispatcherType.REQUEST, DispatcherType.FORWARD}) public class AFilter implements Filter { public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("start filter"); chain.doFilter(request, response); System.out.println("end filter"); }
public void init(FilterConfig fConfig) throws ServletException {} } |
@WebListener() public class AListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { System.out.println("服务器关闭了"); }
public void contextInitialized(ServletContextEvent arg0) { System.out.println("服务器启动了"); } } |
3 Servlet异步处理
Servlet异步处理就是让Servlet在处理费时的请求时不要阻塞,而是一部分一部分的显示。
也就是说,在使用Servlet异步处理之后,页面可以一部分一部分的显示数据,而不是一直卡,等到请求响应结束后一起显示。
在使用异步处理之前,一定要在@WebServlet注解中给出asyncSupported=true,不然默认Servlet是不支持异步处理的。如果存在过滤器,也要设置@WebFilter的asyncSupportedt=true。
@WebServlet(urlPatterns = {"/MyServlet"}, asyncSupported=true) public class MyServlet extends HttpServlet {…} |
注意,响应类型必须是text/html,所以:response.setContentType(“text/html;charset=utf-8”);
使用异步处理大致可以分为两步:
- Servlet正常响应数据;
- Servlet异常响应数据。
在Servlet正常响应数据时,没什么可说的,可通知response.getWriter().print()来向客户端输出,但输出后要使用response.getWriter().flush()刷新,不然数据只是在缓冲区中,不能向客户端发送数据的。
异步响应数据需要使用request.startAsync()方法获取AsyncContext对象。然后调用AsyncContext对象的start()方法启动异步响应,start()方法需要一个Runnable类型的参数。在Runnable的run()方法中给出异步响应的代码。
AsyncContext ac = request.startAsyncContext(request, response); ac.start(new Runnable() {…}); |
注意在异步处理线程中使用response做响应后,要使用response.getWriter().flush()来刷新流,不然数据是不能响应到客户端浏览器的。
asyncContext.start(new Runnable() { public void run() { for(char i = 'a'; i <= 'z'; i++) { try { Thread.sleep(100); asyncContext.getResponse().getWriter().print(i + " "); asyncContext.getResponse().getWriter().flush(); } catch (Exception e) { e.printStackTrace(); } } asyncContext.complete(); } }); |
Tomcat需要知道异步响应是否结束,如果响应不结束,虽然客户端浏览器会看到响应的数据,但是鼠标上只是有个圈圈的不行的转啊转的,表示还没有结束响应。Tomcat会等待到超时为止,这个超时的时间可以通过AsyncContext类的getTimeout()方法获取,Tomcat默认为20000毫秒。当然也可以通过setTimeOut()方法设置,以毫秒为单位。ac.setTimeout(1000*10)。
如果异步线程已经结束了响应,那么可以在异步线程中调用AsyncContext.complete()方法,这样Tomcat就知道异步线程已经完成了工作了。
@WebServlet(urlPatterns = {"/AServlet"}, asyncSupported=true) public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>");
out.flush(); final AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout(1000 * 20); asyncContext.start(new Runnable() { public void run() { try { Thread.sleep(1000); asyncContext.getResponse().getWriter().print("马上开始" + "<br/>"); asyncContext.getResponse().getWriter().flush(); Thread.sleep(2000); } catch (Exception e1) { } for(char i = 'a'; i <= 'z'; i++) { try { Thread.sleep(100); asyncContext.getResponse().getWriter().print(i + " "); asyncContext.getResponse().getWriter().flush(); } catch (Exception e) { e.printStackTrace(); } } asyncContext.complete(); } }); // asyncContext.start(businessHandleThread); // 也可以用这种方法启动异步线程 out.println("Servlet end <br>"); } } |
4 文件上传
Servlet3.0提供了文件上传的处理方案。只需要在Servlet上添加@MultipartConfig注解即可。
@WebServlet(urlPatterns={"/UploadServlet"}) @MultipartConfig(maxFileSize=1024) public class UploadServlet extends HttpServlet { … } |
当然也可以为@MultipartConfig注解指定属性值,它有四个属性:
- int filesizeThreshold:指定缓存的大小,当超出这个大小后,文件会保存到磁盘上;
- String location:指定临时文件的目录;
- long maxFilesize:指定上传单个文件的大小限制,如果上传的谁的超出了这个大小,那么就会抛出异常;
- long maxRequestSize:指定整个表单的大小限制。
当在Servlet上使用了@MultipartConfig注解后,那么就可以使用request.getPart(“fieldName”)来获取<input:file>的内容,其中Part表示一个文件表单项。
<form action="/a1/UploadServlet" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="username"/><br/> 照 片:<input type="file" name="file1" /><br/> <input type="submit" value="提交"/> </form> |
@WebServlet(urlPatterns={"/UploadServlet"}) @MultipartConfig(maxFileSize=1024 * 1024) public class UploadServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username"); response.getWriter().print("size: " + username + "<br/>");
Part part = request.getPart("file1");
response.getWriter().print("size: " + part.getSize() + "<br/>"); response.getWriter().print("type: " + part.getContentType() + "<br/>"); response.getWriter().print("name: " + part.getName() + "<br/>");
String name = part.getHeader("content-disposition"); String fileNameTmp = name.substring(name.indexOf("filename=")+10); String fileName = fileNameTmp.substring(0,fileNameTmp.indexOf("\""));
System.out.println("fileName: " + fileName);
String savepath = this.getServletContext().getRealPath("/uploads"); part.write(savepath + "/" + fileName); } } |