Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了web开发的效率。主要新特性有以下几个:
- 引入注解配置
- 支持web模块化开发
- 程序异步处理
- 改进文件上传API
- 非阻塞式IO读取流
- Websocket实时通信
一、注解配置
Servlet3.0新规范顺应了时代的潮流,使用注解配置,取代混乱的web.xml全局配置。在这之前我们在创建servlet,filter,listener时,都是在web.xml中配置。
//创建一个servlet需要在web.xml中配置如下内容 <servlet> <servlet-name>myFirstServlet</servlet-name> <servlet-class>Test.myServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myFirstServlet</servlet-name> <url-pattern>/aaa</url-pattern> </servlet-mapping></div>
//我们只使用一行代码完成servlet的配置 @WebServlet(name = "myFirstServlet",urlPatterns = {"/aaaa"}) public class myServlet extends HttpServlet { @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getRequestDispatcher("/default.jsp"); rd.forward(req,resp); } }</div>
关于filter和listener的注解配置方法和上述形式一样,在3.0新规范中主要提供了以下一些注解用于配置:
- Websocket :用于配置socket
- WebInitParam :用于配置初始化参数,往往和servlet和filter结合使用
- WebListener :用于配置Listener
- WebFilter :用于配置Filter
- MultipartConfig :用于文件上传(后面会详细介绍)
还有一些,暂时没有涉及,就不列举了
二、Servlet3.0 Web模块化开发
在这之前我们对于web应用中的各个Servlet,Filter,Listener都是需要在web.xml中进行配置,如果只是本项目中的各个点的配置,那倒还好,但是如果我们引入框架,是不是每个框架中的各种配置也是需要在我们的web.xml中配置?这无疑会导致我们唯一的web.xml中内容混乱。Servlet3.0新规范提出了模块化开发,也就是每个Servlet,Filter,Listener都可以有属于自己的配置文件,功能和web.xml一样,它只负责配置当前的servlet。然后我们只需要将配置文件和自己写的Servlet等内容打包成jar,引入到具体项目中即可。(就像我们想要使用了某个功能,引入了从网上下载的jar包到项目中)下面我们看如何使用,由于Servlet,Filter,Listener的配置类似,此处以Servlet为例作为演示:
首先我们写一个servlet类:
public class MyServlet extends HttpServlet { @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ RequestDispatcher rd = req.getRequestDispatcher("/default.jsp"); rd.forward(req,resp); } }</div>
然后我们创建一个web-fragment.xml文件,这就是属于此Servlet自己的配置文件,功能类似于Web.xml,只是这个是私有的。键入以下内容:
<?xml version="1.0" encoding="UTF-8"?> <web-fragment xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd" metadata-complete="false"> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>Test.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/index</url-pattern> </servlet-mapping> </web-fragment></div>
我们可以对比看出,web.xml文件和web-fragment.xml文件除了头部的不一样,一个是web-app,一个是web-fragment,别处几乎一样。我们创建的这个servlet主要负责拦截URL为index的请求,并转向default.jsp页面。
接下来我们看如何打包jar,然后再次为我们项目使用。第一步,无论你是用javac命令还是用IDE编译,首先我们需要将此.java文件编译成class文件。在你的电脑的任意位置创建一个空文件夹,将编译后的class文件及其包复制进去,因为我们MyServlet在Test包下,此处我就是将Test文件夹复制进去(你们需要根据自己建立的文件进行操作)
然后创建一个空文件夹,命名为META-INF,一定要这样命名,因为等我们把jar包引入到项目中之后,一旦web应用启动时,就会去我们引入的jar包的此文件夹下查找web-fragment.xml文件并加载,如果没有找到就不会加载,我们的配置也就不会生效。此时我们文件夹中的内容如下:
将刚刚写完的web-fragment.xml文件复制到META-INF下,然后我们将这两个文件夹压缩成zip格式,然后修改zip为jar即可(因为jar和zip的区别就在于jar中多了一个META-INF文件夹,如果我们已经手动添加了,那他们这两种格式就是一样了)
此处我们使用手动添加META-INF文件夹,然后压缩zip格式的形式来完成打包jar的工作,你也可以使用jdk自带jar命令来完成打包操作,效果是一样的。然后我们将此jar包复制到任意web应用的WEB-INF/lib下,这就是web应用的所有外部引入包所存放的地方。然后我们启动web容器:
结果如上,当我们请求index,拦截器拦截并调向default.jsp页面。这样我们就完成了通过引入外部的jar包而不需要做任何配置,使用了其功能。可能此例并没有很好的展示了这种模块化开发的优势,等到我们学到框架的时候就可以很直观的感受到这种方式的简洁,易于携带。
三、异步处理
在传统的servlet开发中,如果servlet调用了一个耗时很长的逻辑处理方法,那么此servlet必须待在原地等待方法调用结束,这是很低效的一种形式。servlet3.0提出了异步处理的概念,也就是释放了主程序,大大提高了运行效率。
Servlet3.0中异步处理主要是通过接口AsyncContext来实现的,我们可以通过HttpServletRequest对象来过去该接口的实现对象。
AsyncContext getAsyncContext();</div>
在使用异步处理之前,我们还需要配置指定当前的servlet是支持异步处理。有两种方法,第一种是在web.xml中配置
<async-supported>true</async-supported></div>
或者使用webservlet指定属性asyncSupported=true。下面用一个实例演示如何使用servlet的异步处理机制:
@WebServlet(name = "myservlet",urlPatterns = "/index",asyncSupported = true) public class MyServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ resp.setContentType("text/html;charset=UTF-8"); PrintWriter writer = resp.getWriter(); writer.println("servlet 开始:"+new Date()+"<br />"); writer.flush(); AsyncContext asy = req.startAsync(); asy.setTimeout(4000); asy.start(new MyInfo(asy)); writer.println("servlet 结束:"+new Date()+"<br />"); writer.flush(); } }</div>
我们可以看到,这个servlet非常简单,截取URL为index的请求,首先打印启动时间,然后通过request的startAsync方法创建AsyncContext 对象,设置过期时间,启动异步处理。这个线程类代码如下:
public class MyInfo extends Thread { private AsyncContext asyncContext; public MyInfo(AsyncContext as){ this.asyncContext = as; } @Override public void run(){ try { Thread.sleep(3000); PrintWriter pw = asyncContext.getResponse().getWriter(); pw.println("hello walker:"+new Date()+"<br />"); asyncContext.complete(); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOExc