Servlet3.0中Servlet的使用

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,我们都可以给它们设置几个属性。

  • file-size-threshold:数字类型,当文件大小超过指定的大小后将写入到硬盘上。默认是0,表示所有大小的文件上传后都会作为一个临时文件写入到硬盘上。
  • location:指定上传文件存放的目录。当我们指定了location后,我们在调用Part的write(String fileName)方法把文件写入到硬盘的时候可以,文件名称可以不用带路径,但是如果fileName带了绝对路径,那将以fileName所带路径为准把文件写入磁盘。
  • max-file-size:数值类型,表示单个文件的最大大小。默认为-1,表示不限制。当有单个文件的大小超过了max-file-size指定的值时将抛出IllegalStateException异常。
  • 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的内容的话,那它就是空的,系统会抛出异常说找不到对应的文件。

时间: 2025-01-24 06:12:59

Servlet3.0中Servlet的使用的相关文章

Servlet3.0中使用getPart进行文件上传

这个先进些,简单些,但书上提供的例子不能使用,到处弄了弄才行. servlet代码: package cc.openhome; import java.io.InputStream; import java.io.OutputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.File; import java.io.IOException; import ja

Sun向JCP提交Servlet3.0规范—JSR315

js|servlet|规范 Sun 已经向JCP(Java Community Process)提交了Servlet3.0规范--JSR 315(Java Specification Request).其高级目标包括可插拔web框架.EoD(ease of development--易于开发)特征.支持异步和Comet.安全.与其他规范结合.以及其它各项改进.它准备包含在Java EE 6中,Java EE 6的最终版大约于2008第4季度发布.预计不出2007年7月2日就会出评论. 以下是这些

Servlet3.0提供的异步处理

    在以前的Servlet规范中,如果Servlet作为控制器调用了一个耗时的业务方法,那么Servlet必须等到业务方法完全返回之后才会生成响应,这将使得Servlet对业务方法的调用变成一种阻塞式的调用,因此效率比较低.Servlet3.0规范引入了异步处理来解决这个问题,异步处理允许Servlet重新发起一条线程去调用耗时的业务方法,这样就可以避免等待.     Servlet3.0的异步处理是通过AsyncContext类来处理的,Servlet可通过ServletRequest的如

在SpringSide 2.0中使用CXF提供Web Service服务

在前面的随笔中,我讲了我的网站架构,这样的架构决定了我的网站中必须得用到 WebService.比如,在用户注册的时候,用户数据主要是保存在内容服务器中,但是同时也 要将部分数据提交到索引服务器中,这时,就可以让内容服务器访问索引服务器提供的 WebService来提交数据:还可以让内容服务器通过定时任务,访问索引服务器的WebService 来提交统计数据. 我的网站使用SpringSide 2.0开发,在SpringSide 2.0中,默认使用的是XFire来提供 WebService,但是

注释在J2EE5.0中的完美应用

随着J2EE进入5.0时代后,J2SE5.0的很多特性也被广泛应用在J2EE程序中.而J2SE5.0的注释(Annotations)特性就是其中应用最广泛的特性之一. 在以前的J2EE版本中,都是使用大量的配置文件来设置Web程序.EJB等.但这一切在J2SE5.0中得到了彻底的改善.在J2EE5.0的注释中的一些是专门针对Web和EJB程序的.如@Resource, @EJB和@WebServiceRef等.其它的一些注释,如@RunAs和@DeclareRoles则和安全相关. 虽然我们可以

在tomcat上全手工部署Servlet3.0

从头写java文件的方式,编译成CLASS文件,加强对SERVLET容器的理解. 稍后试试JAR和WAR包. 文件内容(跟以前用IDE的一样): HelloServlet.java: package cc.openhome; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet

web项目部署在tomcat6.0中,一运行就报这样的错,各路大神,帮帮小生

问题描述 web项目部署在tomcat6.0中,一运行就报这样的错,各路大神,帮帮小生 20150703 16:42:55 INFO org.springframework.web.struts.ContextLoaderPlugIn.init(ContextLoaderPlugIn.java:218) ContextLoaderPlugIn for Struts ActionServlet 'action_tmp, module '': initialization started 七月 03

tomcat-小白求解Tomcat中Servlet 发生[localhost-startStop-1]

问题描述 小白求解Tomcat中Servlet 发生[localhost-startStop-1] 最近想做jsp 但是无奈第一步就卡住了(是在idea+tomcat 8.0.28下面做的) 其实就是一个最简单的helloworld服务网页,但是tomcat报错,如下: 18-Nov-2015 16:04:10.665 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deplo

关于Ajax技术中servlet末尾的输出流

Ajax的服务器端用PrintWriter out=resp.getWriter()来响应数据的时候,out.print(0).out.print(1)来表示成功或失败,而不用out.write是有原因的,首先来看一下print和write两者的异同点. 共同点是:两者都不刷新页面,只在原来的页面写数据.最终都是重写了抽象类Writer里面的write方法. 区别是:print方法可以将各种类型的数据转换成字符串的形式输出.而重载的write方法只能输出字符.字符数组.字符串等与字符相关的数据.