详解Servlet3.0新特性(从注解配置到websocket编程)

Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了web开发的效率。主要新特性有以下几个:

  1. 引入注解配置
  2. 支持web模块化开发
  3. 程序异步处理
  4. 改进文件上传API
  5. 非阻塞式IO读取流
  6. 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新规范中主要提供了以下一些注解用于配置:

  1. Websocket :用于配置socket
  2. WebInitParam :用于配置初始化参数,往往和servlet和filter结合使用
  3. WebListener :用于配置Listener
  4. WebFilter :用于配置Filter
  5. 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接口提供了以下几个方法帮助我们监控整个过程:

  1. onStartAsync(AsyncEvent event) :当异步调用开始时触发
  2. onComplete(AsyncEvent event) :当异步完成时触发
  3. onError(AsyncEvent event) :当异步调用出错的时候触发
  4. 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属性,他指定了表单参数的编码方式,主要有以下三种:

  1. application/form-data :这是enctype的默认值,指定了这个值就表名表单只会提交所有input标签中的value值,对于我们的文件,提交的就是文件名。
  2. multipart/form-data:这种方式是将参数以二进制存储,上传文件的内容也会被封装成二进制流提交。
  3. 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集合

在上面的程序中,我们使用了其中一些方法。打印了文件类型,文件名,最后将文件保存到本地桌面上。下面是运行的结果截图:

综上就是关于文件上传API的基本使用情况,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • servlet3新特性_动力节点Java学院整理

    Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署.其中有几项特性的引入让开发者感到非常兴奋,同时也获得了 Java 社区的一片赞誉之声: 1.异步处理支持:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程.在接收到请求之后,Servlet

  • 详解Servlet3.0新特性(从注解配置到websocket编程)

    Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了web开发的效率.主要新特性有以下几个: 引入注解配置 支持web模块化开发 程序异步处理 改进文件上传API 非阻塞式IO读取流 Websocket实时通信 一.注解配置 Servlet3.0新规范顺应了时代的潮流,使用注解配置,取代混乱的web.xml全局配置.在这之前我们在创建servlet,filter,listener时,都是在web.xml中配置

  • 详解MySQL8的新特性ROLE

    [MySQL的ROLE解决了什么问题] 假设你是一个职业素养良好的DBA比较同时又比较注重权限管理的话:可能遇到过这样的问题,数据库中有多个开发人员的账号:有一天要建 一个新的schema,如果你希望之前所有的账号都能操作这个schema下的表的话,在mysql-8.0之前你要对第一个账号都单独的赋一次权. mysql-8.0.x所权限抽象了出来用ROLE来表示,当你为ROLE增加新的权限的时候,与这个ROLE关联的所有用户的权限也就一并变化了:针对 上面提到的场景在mysql-8.0.x下只要

  • 详解ES9的新特性之异步遍历Async iteration

    异步遍历 在讲解异步遍历之前,我们先回想一下ES6中的同步遍历. 根据ES6的定义,iteration主要由三部分组成: Iterable 先看下Iterable的定义: interface Iterable { [Symbol.iterator]() : Iterator; } Iterable表示这个对象里面有可遍历的数据,并且需要实现一个可以生成Iterator的工厂方法. Iterator interface Iterator { next() : IteratorResult; } 可

  • 详解c++11新特性之模板的改进

    C++11关于模板有一些细节的改进: 模板的右尖括号 模板的别名 函数模板的默认模板参数 模板的右尖括号 C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误. int main() { std::vector<std::vector<int>> a; // error std::vector<std::vector<int> > b; // ok } 这个我之前都不知道,我开始学编程的时候就已经是C

  • 详解Spring Aop实例之AspectJ注解配置

    上篇<Spring Aop实例之xml配置>中,讲解了xml配置方式,今天来说说AspectJ注解方式去配置spring aop. 依旧采用的jdk代理,接口和实现类代码请参考上篇博文.主要是将Aspect类分享一下: package com.tgb.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aft

  • Android5.0新特性详解之全新的动画

    在Material Design设计中,为用户与app交互反馈他们的动作行为和提供了视觉上的连贯性.Material主题为控件和Activity的过渡提供了一些默认的动画,在android L上,允许自定义这些动画: Touch feedback 触摸反馈 Circular Reveal 圆形展示 Curved motion 曲线运动 View state changes 视图状态变化 Vector Drawables 矢量图动画 Activity transitions 活动转场 触摸反馈 触

  • C#9.0新特性详解——顶级程序语句(Top-Level Programs)

    1 背景与动机 通常,如果只想用C#在控制台上打印一行"Hello World!",这可不是Console.WriteLine("Hello World!");一条语句就可以搞定的,还涉及到其他必要基础代码(如定义类和入口函数Main),例如下面: using System; class Program { static void Main() { Console.WriteLine("Hello World!"); } } 就打印一句"

  • SpringBoot 3.0 新特性内置声明式HTTP客户端实例详解

    目录 http interface 什么是声明式客户端 测试使用 1. maven 依赖 2. 创建 Http interface 类型 3. 注入声明式客户端 4. 单元测试调用 http interface http interface 从 Spring 6 和 Spring Boot 3 开始,Spring 框架支持将远程 HTTP 服务代理成带有特定注解的 Java http interface.类似的库,如 OpenFeign 和 Retrofit 仍然可以使用,但 http inte

  • 关于C# 4.0新特性“缺省参数”的实现详解

    前言 C#4.0关于缺省参数的新特性,相信大家都不会陌生.所谓缺省参数,顾名思义,就是在声明方法的某个参数的时候为之指定一个默认值,在调用该方法的时候如果采用该默认值,你就无须指定该参数.和很多语言层面特性(语法糖)的实现一样,缺省参数也是编译器为我们玩的一个小花招.缺省参数最终体现为两个特殊的自定义特性OptionalAttribute和DefaultParameterValueAttribute . 目录 一.缺省参数的用法 二.实现缺省参数的两个特性:OptionalAttribute和D

  • 简单了解Spring Framework5.0新特性

    SpringFramework5.0是自2013年12月版本4发布之后SpringFramework的第一个主发行版.SpringFramework项目的领导人JuergenHoeller于2016年7月28日宣布了第一个SpringFramework5.0里程碑版本(5.0M1). 现在,将近一年的时间过去以后,我们期盼已久的RC3版本将于2017年7月18日发行.这是路线图规划中SpringFramework5.0首个GA发行版的最后一次发行. 从高层来看,SpringFramework5.

随机推荐