文档章节

Servlet3.0的几个新特性

傻男孩
 傻男孩
发布于 2017/06/22 11:25
字数 2264
阅读 29
收藏 1

注意:Servlet3.0 需要 Tomcat7 和 JavaEE6

本文主要介绍了Servlet3.0发布的诸多新特性中的,以下几个

  • 注解编写Servlet
  • 注解编写Listener(含动态注册Servlet)
  • 注解编写Filter
  • 异步支持
  • 文件上传

下面通过代码来演示各自的具体写法

这是公共的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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">
    <welcome-file-list>
        <welcome-file>login.jsp</welcome-file>
    </welcome-file-list>
</web-app>

这是公共的测试页面 login.jsp

<%@ page language="java" pageEncoding="UTF-8"%>

<a href="${pageContext.request.contextPath}/hello">测试Servlet3.0注解</a>
<br>
<br>
<a href="${pageContext.request.contextPath}/servlet/hello">测试Servlet3.0注解</a>
<br>
<br>
<a href="${pageContext.request.contextPath}/user">测试Servlet3.0动态注册</a>
<br>
<br>
<a href="${pageContext.request.contextPath}/servlet/user">测试Servlet3.0动态注册</a>
<br>
<br>
<a href="${pageContext.request.contextPath}/async">测试Servlet3.0异步支持</a>
<br>
<br>
<form action="${pageContext.request.contextPath}/upload" method="POST" enctype="multipart/form-data">
    <input name="uploadFile" type="file">
    <br/>
    <input type="submit" value="测试Servlet3.0文件上传">
<form>

注解编写Filter

package com.jadyer.demo;
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;
import java.io.IOException;

/**
 * Servlet3.0新特性之注解编写过滤器
 * Created by 玄玉<http://jadyer.cn/> on 2013/06/24 10:39.
 */
@WebFilter(urlPatterns="/*", initParams=@WebInitParam(name="encoding", value="UTF-8"))
public class EncodingFilter implements Filter {
    @Override
    public void destroy() {
        System.out.println("destroy invoked");
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        System.out.println("init invoked");
        System.out.println(config.getInitParameter("encoding"));
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter invoked");
        chain.doFilter(req, resp);
    }
}

注解编写Servlet

@WebServlet 注解常用的有两个参数

  • urlPatterns:用于指定Servlet的访问URL(支持多URL)
  • initParams :用于设定初始参数(支持多参数)

注意:@WebInitParam注解通常不单独使用,而是同 @WebServlet 或者 @WebFilter 组合使用

实际测试:单独使用 @WebInitParam 设定初始参数,设定失败

package com.jadyer.demo;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Servlet3.0新特性之注解编写ervlet
 * Created by 玄玉<http://jadyer.cn/> on 2013/06/23 17:46.
 */
@WebServlet(urlPatterns={"/hello", "/servlet/hello"}, initParams={@WebInitParam(name="savePath", value="D:/upload")})
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 2040116868162606895L;
    private String savePath;

    @Override
    public void init(ServletConfig config) throws ServletException {
        savePath = config.getInitParameter("savePath");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.print("This is Servlet3.0 demo, and the savePath = " + this.savePath);
        out.flush();
        out.close();
    }
}

注解编写Listener

动态注册 Servlet 的功能,也是在 Listener 里面实现的

所以先列出来一个并没有使用 @WebServlet 注解的类 UserServlet.java

package com.jadyer.demo;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 这里并没有使用@WebServlet注解
 * 而是在监听器里面通过Servlet3.0提供的动态注册机制把它动态注册为一个Servlet
 * Created by 玄玉<http://jadyer.cn/> on 2013/06/24 10:39.
 */
public class UserServlet extends HttpServlet {
    private static final long serialVersionUID = 7115756326691777726L;

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println(config.getInitParameter("logPath"));
        System.out.println(config.getInitParameter("savePath"));
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("http://jadyer.cn/");
    }
}

接下来就是核心的监听器 UserListener.java

package com.jadyer.demo;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;

/**
 * Servlet3.0新特性之注解编写监听器以及动态注册Servlet
 * Created by 玄玉<http://jadyer.cn/> on 2013/06/24 10:39.
 */
@WebListener
public class UserListener implements ServletContextListener {
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("服务器关闭时会调用该方法");
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("服务器启动时会调用该方法");
        ServletContext context = sce.getServletContext();
        //注册一个没有使用@WebServlet注解的类为Servlet(第一个参数是Servlet的名字)
        ServletRegistration register = context.addServlet("userServlet", UserServlet.class);
        //为动态注册的Servlet设定访问URL(可设定多个)
        register.addMapping("/user", "/servlet/user");
        //为动态注册的Servlet设定初始参数(可选的,相当于以前的<init-param>)
        register.setInitParameter("logPath", "/app/log");
        register.setInitParameter("savePath", "/app/upload");
    }
}

文件上传

先介绍一下 @MultipartConfig 注解的几个属性

  • maxFileSize :允许上传的单个文件最大值,与所有文件无关,只限制单个文件(单位为byte,默认值是-1,即无限制)
  • maxRequestSize :允许上传的文件最大值,指的是一次请求中的所有文件之和(单位为byte,默认值是-1,即无限制)
  • fileSizeThreshold:设置阈值,达到阈值后会将所上传的文件从临时目录中写到磁盘(单位为byte,默认值是0)
  • location :指定存储所上传文件的目录

关于 location 属性,还分以下两种情况

  1. 如果 part.write(fileName) 传的参数只有上传的文件名,那么最后文件就会被上传到 location 目录中
    若此时 location 所指定的目录不存在,则会报告下面的异常(Tomcat7.x启动时不会报)
    java.io.IOException: The temporary upload location [D:\upload\22] is not valid
  2. 如果 part.write(savePath+"/"+fileName),则无论 location 是否指定目录,最后文件都会被上传到 savePath
    但是,若此时location所指定的目录不存在,那么也会报告第一种情况中的异常,并导致上传文件失败

所以,API 上说 location 是储存文件的目录,但根据上面两种情况来看,我感觉 location 指的是临时文件目录,故不推荐使用

随后我又查看了 Oracle 官方文档,发现该属性指的就是临时文件目录

其官方文档的地址为:http://docs.oracle.com/javaee/6/tutorial/doc/gmhal.html

package com.jadyer.demo;
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;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Servlet3.0新特性之文件上传
 * Created by 玄玉<http://jadyer.cn/> on 2013/06/24 15:19.
 */
@WebServlet(urlPatterns="/upload")
//标明此Servlet支持文件上传
@MultipartConfig(fileSizeThreshold=1024*1024*2, maxFileSize=1024*1024*20, maxRequestSize=1024*1024*200)
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 9007276067357814862L;

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        //回显上传结果给浏览器
        String uploadResult;
        PrintWriter out = resp.getWriter();
        resp.setContentType("text/html; charset=UTF-8");
        try{
            //获取上传的文件的Part,下面的操作都是以这个Part为中心的
            Part part = req.getPart("uploadFile");

            //本例中它的值是form-data; name="uploadFile"; filename="玄玉.png"
            String _str = part.getHeader("content-disposition");
            //获取上传的文件真实名字(含后缀)
            String fileName = _str.substring(_str.lastIndexOf("=")+2, _str.lastIndexOf("\""));

            //指定上传的文件的存储目录并确保其存在
            String savePath = "D:/upload/";
            File savePathFolder = new File(savePath);
            if(!savePathFolder.exists()){
                savePathFolder.mkdirs();
            }
            //上传文件(写入磁盘)
            part.write(savePath + "/" + fileName);
            uploadResult = "上传完毕<br/>" +
                    "上传的文件Part=" + part.getName() + "<br/>" +
                    "上传的文件名称=" + fileName + "<br/>" +
                    "上传的文件大小=" + part.getSize() + "<br/>" +
                    "上传的文件类型=" + part.getContentType();
        }catch(IllegalStateException ise){
            uploadResult = "上传失败,失败原因为:<br/>" + ise.getMessage();
        }
        out.print(uploadResult);
        out.flush();
        out.close();
    }
}

异步支持

Servlet2.5 中也可以单独启动一个线程去执行耗时的任务,接着Servlet会继续往下执行

执行完最后一行代码时,Servlet就会把响应输出给请求方,而那个单独启动的线程的任何执行结果都不会反映给请求方

此其一

其二

若不单独启动线程去执行耗时任务,而是将耗时操作串行放到Servlet方法中执行的话,也是不可取的

因为Servlet容器会管理着一个线程池,处理我们请求的Servlet所依附的线程都是来自于这个线程池的

如果某个操作过于耗时,那么线程池里面的资源就会被占用掉,所以 Servlet3.0 就有了异步支持

Servlet3.0 中的异步支持也是单独启动线程执行耗时任务,但是当Servlet执行到最后一行代码时

它提供了一些机制会判断那个线程是否执行完毕,直到线程执行完毕后才会响应所有结果给请求方

package com.jadyer.demo;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Servlet3.0新特性之异步支持
 * Created by 玄玉<http://jadyer.cn/> on 2013/06/23 17:47.
 */
//默认不支持异步,需手工开启:asyncSupported=true
@WebServlet(urlPatterns={"/async"}, asyncSupported=true)
public class AsyncServet extends HttpServlet {
    private static final long serialVersionUID = 3314056681141922826L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.println("doGet starts:" + new Date());
        out.flush();
        System.out.println("第一步mark");

        //启动一个异步操作的上下文对象AsyncContext
        AsyncContext context = req.startAsync();
        //添加一个监听器(奇怪:AsyncListener竟然没有适配器)
        context.addListener(new AsyncListener(){
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                PrintWriter out = asyncEvent.getSuppliedResponse().getWriter();
                out.println("async succes:" + new Date());
                out.flush();
                /*
                 * 重点:在这里关闭流
                 * -----------------------------------------------------------------------------------------
                 * 1、自打请求进入doGet()开始,直到客户端收到响应为止
                 *    整个过程的任意位置获得的PrintWriter和ServletResponse对象都是一个(通过输出对象引用看出来的)
                 * 2、第一个mark处声明的PrintWriter对象不能在第二个mark处关闭,只能在第4个或第5个mark处关闭
                 *    否则异步支持就会失败(如果是在第5处关闭,那么第5处的out.println()也会响应给客户端)
                 * -----------------------------------------------------------------------------------------
                 */
                out.close();
                System.out.println("第五步mark");
            }
            @Override
            public void onError(AsyncEvent arg0) throws IOException {}
            @Override
            public void onStartAsync(AsyncEvent arg0) throws IOException {}
            @Override
            public void onTimeout(AsyncEvent arg0) throws IOException {}
        });
        //也可以用这种方法启动异步线程
        //context.start(new HelloAsyncThread(context));
        new Thread(new HelloAsyncThread(context)).start();

        out.println("doGet   ends:" + new Date());
        out.flush();
        System.out.println("第二步mark");
    }

    private static class HelloAsyncThread implements Runnable {
        private AsyncContext context;
        HelloAsyncThread(AsyncContext context){
            this.context = context;
        }
        @Override
        public void run() {
            try {
                PrintWriter out = context.getResponse().getWriter();
                out.println("async starts:" + new Date());
                out.flush();
                System.out.println("第三步mark");

                //模拟耗时操作:让线程睡3s
                //Thread.sleep(3000);
                TimeUnit.SECONDS.sleep(3);

                out.println("async   ends:" + new Date());
                out.flush();
                System.out.println("第四步mark");

                //调用complete()就能够告诉Servlet异步处理已完毕,否则Servlet还在傻傻等待,浪费时间
                context.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

© 著作权归作者所有

傻男孩
粉丝 2
博文 47
码字总数 52350
作品 0
深圳
高级程序员
私信 提问
关于Servlet3.0与Servlet3.1大神们你们怎么选择?

问题背景: Servlet3.1新特性——非阻塞式IO,ServletInputStream抽象类多了几个方法isFinished()、isReady()、setReadListener(ReadListener listener)。这些在之前的Servlet3.0里是没有的。...

雨花石
2017/01/10
1K
1
Servlet3.0异步的实现需要http1.1的支持?

今天我在学习servlet3.0的新特性:servlet3.0对异步的支持。 网上有人说,servlet3.0对异步的支持需要http1.1。 现在我把servelt3.0对异步支持的原理也理解的差不多了,并且查阅了http1.1相比...

张凯乐
2016/12/30
222
3
Servlet3.0新特性剖析

Servlet3.0规范的新特性主要是为了3个目的: 1.简化开发 2.便于布署 3.支持Web2.0原则 为了简化开发流程,Servlet3.0引入了注解(annotation),这使得web布署描述符web.xml不在是必须的选择...

长平狐
2012/09/03
195
0
Tomcat无法启动,报错java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory

前面一段时间看到 Tomcat7.0 发布了几个测试版,由于没有稳定,也就没有测试了,今天看到新闻,看到 Tomcat7.0 正式版已经发布了,到官网上下载下来,看看效果如何。 < XMLNAMESPACE PREFIX ...

鉴客
2012/06/05
10.9K
1
@WebListener 为什么不起作用

@WebListener 是servlet3.0后的新特性,需要在web.xml中配置,如下: <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:w......

Beaver_
2015/03/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

RocketMQ的事务投递

RocketMQ的事务投递 这是阿里的分布式事务图: 1、A服务先发送个Half Message给Brock端,消息中携带 B服务 即将要+100元的信息。 2、当A服务知道Half Message发送成功后,那么开始第3步执行本...

春哥大魔王的博客
24分钟前
1
0
Qt编写自定义控件31-面板仪表盘控件

一、前言 在Qt自定义控件中,仪表盘控件是数量最多的,写仪表盘都写到快要吐血,可能是因为各种工业控制领域用的比较多吧,而且仪表盘又是比较生动直观的,这次看到百度的echart中有这个控件...

飞扬青云
29分钟前
3
0
DisplayPort 迎来重大更新,数据带宽性能提高3倍

VESA宣布了他们对DisplayPort接口三年来的第一次重大更新。 与DP 1.4a相比,DisplayPort 2.0提供了三倍于DP 1.4a的数据带宽性能,支持超过8K的分辨率,更高的刷新率和更高分辨率的HDR,以及其...

linuxCool
36分钟前
1
0
《Linux就该这么学》2019年7月20日第八天上课笔记

du命令 du -sh /newFS/ 察看文件/文件夹数据占用量 SWAP 交换分区的设置 磁盘容量配额 RHEL 5/6 usrquota RHEL 7 quota 软硬连接 ln 软 指针指向inode 硬 建立新的inode RAID 0 1 5 1+0...

2lodoss
39分钟前
1
0
适合钱包应用开发的ERC20代币数据集

Erc20Tokens数据集包含超过1000种主流的以太坊ERC20代币的描述数据清单和图标,可用于钱包等区块链应用的开发,支持使用Java、Python、Php、NodeJs、C#等各种开发语言查询主流ERC20代币的相关...

汇智网教程
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部