这里有新鲜出炉的精品教程, 程序狗速度看过来!
Servlet3.0 的出现是 servlet 史上最大的变革, 其中的许多新特性大大的简化了 web 应用的开发, 为广大劳苦的程序员减轻了压力, 提高了 web 开发的效率
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>
- // 我们只使用一行代码完成 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);
- }
- }
关于 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);
- }
- }
然后我们创建一个 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>
我们可以对比看出, 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();
在使用异步处理之前, 我们还需要配置指定当前的 servlet 是支持异步处理有两种方法, 第一种是在 web.xml 中配置
<async-supported>true</async-supported>
或者使用 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();
- }
- }
我们可以看到, 这个 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 (IOException e) {
- e.printStackTrace();
- }
- }
- }
一个构造方法接受 AsyncContext 对象, run 方法中, 先打印一句话然后结束异步调用我们看看结果:
通过时间我们可以看到 servlet 开始和结束几乎同时, 而我们的异步处理却相差三秒钟, 正是我们 sleep 的三秒钟虽然我们实现了在 servlet 中异步调用别的线程来处理一些逻辑, 但是我们还是不能完全控制整个异步处理中的各个过程, 比如何时开始, 何时结束等 Servlet3.0 中的 AsyncListener 接口提供了以下几个方法帮助我们监控整个过程:
onStartAsync(AsyncEvent event) : 当异步调用开始时触发
onComplete(AsyncEvent event) : 当异步完成时触发
onError(AsyncEvent event) : 当异步调用出错的时候触发
onTimeout(AsyncEvent event): 当异步调用超时时候触发
想要实现监控异步调用, 首先需要编写一个类继承自 AsyncListener 然后实现如上四个方法, 之后这个类就是一个可以监控异步调用的监听器
- public class MyAsyncListener implements AsyncListener {
- public void onComplete(AsyncEvent var1) throws IOException{
- System.out.println("异步调用结束了");
- }
- public void onTimeout(AsyncEvent var1) throws IOException{
- System.out.println("异步调用超时了");
- }
- public void onError(AsyncEvent var1) throws IOException{
- System.out.println("异步调用出错了");
- }
- public void onStartAsync(AsyncEvent var1) throws IOException{
- System.out.println("异步调用开始了");
- }
- }
在我们的 Servlet 主程序中使用以下语句绑定此异步监听器:
asy.addListener(new MyAsyncListener());
此时异步处理的四个结点的动态, 我们都是实时掌控的但是需要注意一点的是: 虽然理论上我们是可以监听四个状态的, 但是其实异步开始这个事件我们是没法监听的, 也就是异步开始的方法永远不会被触发, 原因是在注册 AsyncContext 的时候, 已经开始了异步, 然而我们却在注册之后才绑定监听器, 自然是不能监听到异步开始这个事件的
四文件上传 API
对于传统的文件上传, 我们是需要借助于外部工具的, 例如: common-fileupload 等自从 servlet3.0 新规范以来, 改进了文件上传 API
- <body>
- <h1 > 这是 index 页面</h1>
- <form method="post" action="/submit" enctype="multipart/form-data">
姓名:<input type="text" name="name" /><br /><br />
头像:<input type="file" name="mFile" /><br /><br />
- <input type="submit" value="提交" />
- </form>
- </body>
我们知道, 在 html 中上传文件的表单用 type="file" 来指定, 这是一点, 还有一点就是 from 标签的 enctype 属性, 他指定了表单参数的编码方式, 主要有以下三种:
application/form-data : 这是 enctype 的默认值, 指定了这个值就表名表单只会提交所有 input 标签中的 value 值, 对于我们的文件, 提交的就是文件名
multipart/form-data: 这种方式是将参数以二进制存储, 上传文件的内容也会被封装成二进制流提交
text/plain: 这种方式主要用于发送邮件
对于需要上传文件功能的我们自然选择第二个参数值, 正如上述代码展示的一样下面我们写一个 servlet 用于处理上传的信息
- @WebServlet(name = "myServlet",urlPatterns = {"/submit"})
- @MultipartConfig // 处理文件上传的 servlet 需要配置此注解
- public class FileUpload extends HttpServlet {
- public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
- resp.setContentType("text/html;charset=UTF-8");
- PrintWriter writer = resp.getWriter();
- Part part = req.getPart("mFile");
- writer.println("文件类型:"+part.getContentType()+"<br />");
- writer.println("文件名:"+part.getName()+"<br />");
- part.write("C:\\Users\\Administrator\\Desktop\\photo.jpg");
- }
- }
在 servlet3.0 中采用 Part 接口来处理文件上传, 可以通过 HtppServletRequest 的以下两个方法来获取此接口对象:
- Part getPart(String name);
- Collection<Part> getParts();
一个 part 对应于我们一个文件上传域, 也就是一个 input 类型为 file 的元素 part 中有以下一些方法:
- String getContentType(); // 返回文件类型, 如 image/png
- String getName(); // 返回文件名
- String getSubmittedFileName();
- long getSize(); // 返回文件的大小
- void write(String var1) throws IOException; // 将文件写入到服务器磁盘
- void delete() throws IOException; // 删除此文件
- String getHeader(String var1); // 获取指定文件名的值
- Collection<String> getHeaders(String var1); // 获取指定文件名的所有的值
- Collection<String> getHeaderNames(); // 获取所有 Header 的 name 集合
在上面的程序中, 我们使用了其中一些方法打印了文件类型, 文件名, 最后将文件保存到本地桌面上下面是运行的结果截图:
来源: http://www.phperz.com/article/18/0314/357390.html