Servlet3.0中Servlet的使用
目录
1.注解配置
2.异步调用
3.文件上传
相对于之前的版本,Servlet3.0中的Servlet有以下改进:
l 支持注解配置。
l 支持异步调用。
l 直接有对文件上传的支持。
在这篇文章中我将主要讲这三方面的应用示例。
1.注解配置
在以往我们的Servlet都需要在web.xml文件中进行配置(Servlet3.0同样支持),但是在Servlet3.0中引入了注解,我们只需要在对应的Servlet类上使用@WebServlet注解进行标记,我们的应用启动之后就可以访问到该Servlet。对于一个@WebServlet而言,有一个属性是必须要的,那就是它的访问路径。@WebServlet中有两个属性可以用来表示Servlet的访问路径,分别是value和urlPatterns。value和urlPatterns都是数组形式,表示我们可以把一个Servlet映射到多个访问路径,但是value和urlPatterns不能同时使用。如果同时使用了value和urlPatterns,我们的Servlet是无法访问到的。下面是一个使用@WebServlet的简单Servlet示例。
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- *
- * Servlet3.0支持使用注解配置Servlet。我们只需在Servlet对应的类上使用@WebServlet进行标注,
- * 我们就可以访问到该Servlet了,而不需要再在web.xml文件中进行配置。@WebServlet的urlPatterns
- * 和value属性都可以用来表示Servlet的部署路径,它们都是对应的一个数组。
- */
- @WebServlet(name="exampleServlet", urlPatterns="/servlet/example")
- public class ExampleServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- this.doPost(request, response);
- }
- @Override
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- response.getWriter().write("Hello User.");
- }
- }
初始化参数
使用@WebServlet时也可以配置初始化参数,它是通过@WebServlet的initParams参数来指定的。initParams是一个@WebInitParam数组,每一个@WebInitParam代表一个初始化参数。
- import java.io.IOException;
- import java.util.Enumeration;
- 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;
- /**
- * 带初始化参数的Servlet
- * WebServlet的属性initParams可以用来指定当前Servlet的初始化参数,它是一个数组,
- * 里面每一个@WebInitParam表示一个参数。
- */
- @WebServlet(value="/servlet/init-param", initParams={@WebInitParam(name="param1", value="value1")})
- public class WebInitParamServlet extends HttpServlet {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- this.doPost(req, resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- Enumeration<String> paramNames = this.getServletConfig().getInitParameterNames();
- String paramName;
- while (paramNames.hasMoreElements()) {
- paramName = paramNames.nextElement();
- resp.getWriter().append(paramName + " = " + this.getServletConfig().getInitParameter(paramName));
- }
- resp.getWriter().close();
- }
- }
2.异步调用
在Servlet3.0中,在Servlet内部支持异步处理。它的逻辑是当我们请求一个Servlet时,我们的Servlet可以先返回一部分内容给客户端。然后在Servlet内部异步处理另外一段逻辑,等到异步处理完成之后,再把异步处理的结果返回给客户端。这意味着当我们的Servlet在处理一段比较费时的业务逻辑时,我们可以先返回一部分信息给客户端,然后异步处理费时的业务,而不必让客户端一直等待所有的业务逻辑处理完。等到异步处理完之后,再把对应的处理结果返回给客户端。
异步调用是通过当前HttpServletRequest的startAsync()方法开始的,它返回一个AsyncContext。之后我们可以调用AsyncContext的start()方法来新起一个线程进行异步调用。在新线程内部程序的最后我们最好是调用一下当前AsyncContext的complete()方法,否则异步调用的结果需要等到设置的超时时间过后才会返回到客户端。另外当异步调用超时以后会接着调用异步任务,即新起的线程。
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.AsyncContext;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- * 支持异步返回的Servlet
- * 对于Servlet的异步返回,首先我们必须指定@WebServlet的asyncSupported属性为true(默认是false),同时在它之前的Filter
- * 的asyncSupported属性也必须是true,否则传递过来的request就是不支持异步调用的。
- *
- */
- @WebServlet(value="/servlet/async", asyncSupported=true)
- public class AsyncServlet extends HttpServlet {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- this.doPost(req, resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setContentType("text/plain;charset=UTF-8");
- final PrintWriter writer = resp.getWriter();
- writer.println("异步之前输出的内容。");
- writer.flush();
- //开始异步调用,获取对应的AsyncContext。
- final AsyncContext asyncContext = req.startAsync();
- //设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
- asyncContext.setTimeout(10*1000L);
- //新起线程开始异步调用,start方法不是阻塞式的,它会新起一个线程来启动Runnable接口,之后主程序会继续执行
- asyncContext.start(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(5*1000L);
- writer.println("异步调用之后输出的内容。");
- writer.flush();
- //异步调用完成,如果异步调用完成后不调用complete()方法的话,异步调用的结果需要等到设置的超时
- //时间过了之后才能返回到客户端。
- asyncContext.complete();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
- writer.flush();
- }
- }
对于一个Servlet如果要支持异步调用的话我们必须指定其asyncSupported属性为true(默认是false)。使用@WebServlet注解标注的Servlet我们可以直接指定其asyncSupported属性的值为true,如:
@WebServlet(value=”/servlet/async”, asyncSupported=true)。而对于在web.xml文件中进行配置的Servlet来说,我们需要在配置的时候指定其asyncSupported属性为true。
- <servlet>
- <servlet-name>xxx</servlet-name>
- <servlet-class>xxx</servlet-class>
- <async-supported>true</async-supported>
- </servlet>
- <servlet-mapping>
- <servlet-name>xxx</servlet-name>
- <url-pattern>xxx</url-pattern>
- </servlet-mapping>
Servlet的异步调用程序的关键是要调用当前HttpServletRequest的startAsync()方法。至于利用返回的AsyncContext来新起一个线程进行异步处理就不是那么的必须了,因为在HttpServletRequest startAsync()之后,我们可以自己新起线程进行异步处理。
- @WebServlet(value="/servlet/async", asyncSupported=true)
- public class AsyncServlet extends HttpServlet {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- this.doPost(req, resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setContentType("text/plain;charset=UTF-8");
- final PrintWriter writer = resp.getWriter();
- writer.println("异步之前输出的内容。");
- writer.flush();
- //开始异步调用,获取对应的AsyncContext。
- final AsyncContext asyncContext = req.startAsync();
- //设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
- asyncContext.setTimeout(10*1000L);
- Runnable r = new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(5*1000L);
- writer.println("异步调用之后输出的内容。");
- writer.flush();
- //异步调用完成
- asyncContext.complete();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- Thread t = new Thread(r);
- //开启自己的线程进行异步处理
- t.start();
- writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
- writer.flush();
- }
- }
异步调用监听器
当我们需要对异步调用做一个详细的监听的时候,比如监听它是否超时,我们可以通过给AsyncContext设置对应的监听器AsyncListener来实现这一功能。AsyncListener是一个接口,里面定义了四个方法,分别是针对于异步调用开始、结束、出错和超时的。
- import java.io.IOException;
- import java.io.PrintWriter;
- 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;
- /**
- * 支持异步返回的Servlet
- * 对于Servlet的异步返回,首先我们必须指定@WebServlet的asyncSupported属性为true(默认是false),同时在它之前的Filter
- * 的asyncSupported属性也必须是true,否则传递过来的request就是不支持异步调用的。
- *
- */
- @WebServlet(value="/servlet/async2", asyncSupported=true)
- public class AsyncServlet2 extends HttpServlet {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- this.doPost(req, resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- resp.setContentType("text/plain;charset=UTF-8");
- final PrintWriter writer = resp.getWriter();
- writer.println("异步之前输出的内容。");
- writer.flush();
- //开始异步调用,获取对应的AsyncContext。
- final AsyncContext asyncContext = req.startAsync();
- //设置当前异步调用对应的监听器
- asyncContext.addListener(new MyAsyncListener());
- //设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
- asyncContext.setTimeout(10*1000L);
- //新起线程开始异步调用,start方法不是阻塞式的,它会新起一个线程来启动Runnable接口,之后主程序会继续执行
- asyncContext.start(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(5*1000L);
- writer.println("异步调用之后输出的内容。");
- writer.flush();
- //异步调用完成
- asyncContext.complete();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
- writer.flush();
- }
- /**
- * 异步调用对应的监听器
- * @author Yeelim
- * @date 2014-2-8
- * @mail yeelim-zhang@todaytech.com.cn
- */
- private class MyAsyncListener implements AsyncListener {
- @Override
- public void onComplete(AsyncEvent event) throws IOException {
- System.out.println("异步调用完成……");
- event.getSuppliedResponse().getWriter().println("异步调用完成……");
- }
- @Override
- public void onError(AsyncEvent event) throws IOException {
- System.out.println("异步调用出错……");
- event.getSuppliedResponse().getWriter().println("异步调用出错……");
- }
- @Override
- public void onStartAsync(AsyncEvent event) throws IOException {
- System.out.println("异步调用开始……");
- event.getSuppliedResponse().getWriter().println("异步调用开始……");
- }
- @Override
- public void onTimeout(AsyncEvent event) throws IOException {
- System.out.println("异步调用超时……");
- event.getSuppliedResponse().getWriter().println("异步调用超时……");
- }
- }
- }
注:
对于正常执行的异步调用而言上述代码中开始是没有监听到的,只有在异步调用超时,重新执行异步任务的时候才有监听到异步调用的开始。不过如果需要监听异步第一次开始的话,我们可以在异步调用开始的时候做相应的监听器监听到异步调用开始时需要做的内容。
3.文件上传
在Servlet3.0中上传文件变得非常简单。我们只需通过request的getPart(String partName)获取到上传的对应文件对应的Part或者通过getParts()方法获取到所有上传文件对应的Part。之后我们就可以通过part的write(String fileName)方法把对应文件写入到磁盘。或者通过part的getInputStream()方法获取文件对应的输入流,然后再对该输入流进行操作。要使用request的getPart()或getParts()方法对上传的文件进行操作的话,有两个要注意的地方。首先,用于上传文件的form表单的enctype必须为multipart/form-data;其次,对于使用注解声明的Servlet,我们必须在其对应类上使用@MultipartConfig进行标注,而对于在web.xml文件进行配置的Servlet我们也需要指定其multipart-config属性,如:
- <servlet>
- <servlet-name>xxx</servlet-name>
- <servlet-class>xxx.xxx</servlet-class>
- <multipart-config></multipart-config>
- </servlet>
- <servlet-mapping>
- <servlet-name>xxx</servlet-name>
- <url-pattern>/servlet/xxx</url-pattern>
- </servlet-mapping>
不管是基于注解的@MultipartConfig,还是基于web.xml文件配置的multipart-config,我们都可以给它们设置几个属性。
l file-size-threshold:数字类型,当文件大小超过指定的大小后将写入到硬盘上。默认是0,表示所有大小的文件上传后都会作为一个临时文件写入到硬盘上。
l location:指定上传文件存放的目录。当我们指定了location后,我们在调用Part的write(String fileName)方法把文件写入到硬盘的时候可以,文件名称可以不用带路径,但是如果fileName带了绝对路径,那将以fileName所带路径为准把文件写入磁盘。
l max-file-size:数值类型,表示单个文件的最大大小。默认为-1,表示不限制。当有单个文件的大小超过了max-file-size指定的值时将抛出IllegalStateException异常。
l max-request-size:数值类型,表示一次上传文件的最大大小。默认为-1,表示不限制。当上传时所有文件的大小超过了max-request-size时也将抛出IllegalStateException异常。
上面的属性是针对于web.xml中配置Servlet而言的,其中的每一个属性都对应了multipart-config元素下的一个子元素。对于基于注解配置的Servlet而言,@MultipartConfig的属性是类型的,我们只需把上述对应属性中间的杠去掉,然后把对应字母大写即可,如maxFileSize。
下面给出Servlet3.0中文件上传的一个示例。
Html:
- <form method="post" action="servlet/upload" enctype="multipart/form-data">
- <input type="file" name="upload"/>
- <input type="submit" value="upload"/>
- </form>
对应Servlet:
- @WebServlet("/servlet/upload")
- @MultipartConfig
- public class FileUploadServlet extends HttpServlet {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- req.setCharacterEncoding("UTF-8");
- Part part = req.getPart("upload");
- //格式如:form-data; name="upload"; filename="YNote.exe"
- String disposition = part.getHeader("content-disposition");
- System.out.println(disposition);
- String fileName = disposition.substring(disposition.lastIndexOf("=")+2, disposition.length()-1);
- String fileType = part.getContentType();
- long fileSize = part.getSize();
- System.out.println("fileName: " + fileName);
- System.out.println("fileType: " + fileType);
- System.out.println("fileSize: " + fileSize);
- String uploadPath = req.getServletContext().getRealPath("/upload");
- System.out.println("uploadPath" + uploadPath);
- part.write(uploadPath + File.separator +fileName);
- }
- }
对于Servlet3.0中的文件上传还有一个需要注意的地方,当我们把Part写入到硬盘以后,我们原先的Part(也就是之前的临时文件)可能已经删了,这个时候如果我们再次去访问Part的内容的话,那它就是空的,系统会抛出异常说找不到对应的文件。
Servlet3.0中Servlet的使用的更多相关文章
- 关于servlet3.0中的异步servlet
刚看了一下维基百科上的介绍,servlet3.0是2009年随着JavaEE6.0发布的: 到现在已经有六七年的时间了,在我第一次接触java的时候(2011年),servlet3.0就已经出现很久了 ...
- 【JavaWeb】Servlet3.0中注解驱动开发
一.概述 二.@WebServlet注解 三.共享库/运行时插件 2.1 注册Servlet 2.2 注册监听器 2.3 注册过滤器 一.概述 Servlet3.0中引入了注解开发 二.@WebSer ...
- web.xml在Servlet3.0中的新增元素
metadata-complete: 当属性为true时,该Web应用将不会加载注解配置的Web组件(如Servlet.Filter.Listener) 当属性为false时,将加载注解配置的Web组 ...
- 转载 Servlet3.0中使用注解配置Servle
转载地址:http://www.108js.com/article/article10/a0021.html?id=1496 开发Servlet3的程序需要一定的环境支持.Servlet3是Java ...
- Servlet3.0中使用getPart进行文件上传
这个先进些,简单些,但书上提供的例子不能使用,到处弄了弄才行. servlet代码: package cc.openhome; import java.io.InputStream; import j ...
- 利用servlet3.0上传,纯原生上传,不依赖任何第三方包
tomcat7里面自带的servlet3.0.jar,支持很多新特性,例如,annotation配置servlet,上传,异步等等.... 如果你的tomcat版本低于7的话,单独在项目中引入serv ...
- 项目支持Servlet3.0的新特性
一.Servlet3.0介绍 Servlet3.0是Java EE6规范的一部分,Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署描述 ...
- Servlet3.0学习总结——基于Servlet3.0的文件上传
Servlet3.0学习总结(三)——基于Servlet3.0的文件上传 在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileu ...
- Servlet3.0的新特性
注意:Servlet3.0的项目一定要使用Tomcat7.0才能看到效果!! 1.新增标注支持 在Servlet3.0的部署描述文件web.xml的顶层标签<web-app>中有一 ...
随机推荐
- 【有人@我】Android中高亮变色显示文本中的关键字
应该是好久没有写有关技术类的文章了,前天还有人在群里问我,说群主很长时间没有分享干货了,今天分享一篇Android中TextView在大段的文字内容中如何让关键字高亮变色的文章 ,希望对大家有所帮助, ...
- Linq连接查询之左连接、右连接、内连接、全连接、交叉连接、Union合并、Concat连接、Intersect相交、Except与非查询
内连接查询 内连接与SqL中inner join一样,即找出两个序列的交集 Model1Container model = new Model1Container(); //内连接 var query ...
- 转一篇关于Unity的PlayMaker
这篇文章转自http://va.lent.in/should-you-use-playmaker-in-production/ 此文作者大概深受其苦,吐槽了playmaker的多个蛋疼的地方,这其实说 ...
- ThreadLocal类学习笔记
这个类在java1.2中就出现了,线程独有的变量(每个线程都有一份变量),使用它的好处之一就是可以少传许多参数. 在哪里用到它呢?有连接池的地方就有它的身影,连接池包括数据库连接池,网络连接池等. i ...
- vijos P1009清帝之惑之康熙
</pre>背景康熙是中国历史乃至世界历史中最伟大的帝王之一,清除螯拜,撤除三藩,统一台湾,平定准葛尔叛乱:与此同时,出众的他也被世界各国遣清使臣所折服.康熙是历史上少有的全人,不仅文武兼 ...
- 【前端积累】createElement createTextNode
<!DOCTYPE html> <html><!--树根--> <head> <meta charset="utf-8"> ...
- ubuntu搭建java开发环境
最近因为要编译Android源码,但是报错因为Java版本低于1.7.x而不能进行编译,于是进行Java版本更改. 安装前软件环境: Ubuntu14.02,Java 1.6.0_29 目标软件环境: ...
- (转)ElasticSearch学习
ElasticSearch是一个基于Lucene构建的开源,分布式,RESTful搜索引擎.设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便.支持通过HTTP使用JSON进行数据索引 ...
- 转: linux内核版本本地版本号的检查——setlocalversion
转载:http://blog.csdn.net/adaptiver/article/details/7225980 1. 引子 编译2.6.35.7 kernel版本的时候发现,"2.6 ...
- dede使用方法----更换模板
刚开始接触dede的时候,自己一直在纠结怎么更换自己的模板啊.后面在秀站网上找到了一个比较好的更换模板的文字教程.下面就我自己做的进行写的笔记. 准备:一个你自己做好的静态版网站,我们假设它的名称是w ...