Java后端Java技术升华面试题

Servlet技术

2019-04-19  本文已影响181人  辽A丶孙悟空
一、Servlet概述

Servlet是基于Java语言的Web服务器端编程技术,按照Java EE规范定义,Servlet是运行在Servlet容器中的Java类,它能处理Web客户的HTTP请求,并产生HTTP响应。


  • Servlet对请求的处理和响应过程分为如下几个步骤:
    接收HTTP请求;
    取得请求信息,包括请求头和请求参数数据;
    调用其他Java类方法,完成具体的业务功能;
    实现到其他Web组件的跳转(包括重定向或请求转发);
    生成HTTP响应(包括HTML或非HTML响应)。

1.创建Java Web项目
2.创建Servlet
3.实现doPost()或doGet()方法
4.声明配置Servlet:
3.x版本以上规范中使用注解声明配置

@WebServlet("/hello")

2.5版本规范中采用web.xml配置文件声明配置
5.部署运行Servlet

Servlet是使用Servlet API(应用程序设计接口)及相关类和方法的Java程序。
Servlet API包含两个软件包:

  • javax.servlet包
    定义了所有Servlet类都必须实现或继承的通用接口和类
  • javax.servlet.http包
    定义了采用HTTP协议通信的HttpServlet类。


    Servlet API的主要接口和类之间的关系
Servlet声明配置样例
  • loadOnStartup
    指定Servlet的加载顺序。当此选项没有指定时,表示容器在该Servlet第一次被请求时才加载;当值为0或者大于0时,表示容器在应用启动时就加载这个Servlet。值越小,启动该servlet的优先级越高。原则上不同的Servlet应该使用不同的启动顺序数字
  • init(ServletConfig config)
    Servlet初始化方法,在Servlet实例化后,容器调用该方法进行Servlet的初始化,init()方法只能被调用一次,如果此方法没有正常结束,就会抛出一个ServletException异常,一旦抛出该异常,Servlet将不再执行,随后对其进行再次调用会,容器会重新载入并再次运行init()方法。
  • Servlet的映射路径
urlPatterns= {"/hello","/hello.do","/hello/hello.html"}
urlPatterns= {"/hello","*.do"}

1.精确匹配:
/hello
访问路径:http://localhost:8080/HelloServlet/hello
/hello/hello.do
访问路径:http://localhost:8080/HelloServlet/hello/hello.do
2.模糊匹配:
/*
访问路径:http://localhost:8080/HelloServlet/任意路径
/hello/*
访问路径: http://localhost:8080/HelloServlet/hello/任意路径
.do、.action、*.后缀名
访问路径: http://localhost:8080/HelloServlet/任意路径.do

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >xmlns="http://xmlns.jcp.org/xml/ns/javaee" >xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee >http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" >version="3.1">
 <display-name>ch02-HelloServlet2.5</display-name>
 <servlet>
  <servlet-name>HelloServlet</servlet-name>
  <servlet-class>com.neuedu.servlet.HelloServlet</servlet-class>
  <load-on-startup>0</load-on-startup>
  <init-param>
      <param-name>name</param-name>
      <param-value>neuedu</param-value>
  </init-param>
  <init-param>
      <param-name>email</param-name>
      <param-value>xxx@neuedu.com</param-value>
  </init-param>
 </servlet>
 <servlet-mapping>
  <servlet-name>HelloServlet</servlet-name>
  <url-pattern>/hello.do</url-pattern>
 </servlet-mapping>
</web-app>
  • 在web.xml中通过“<servlet> </servlet>”元素声明Servlet
    <display-name>:指定该Servlet的显示名,通常配合工具使用,等价于@WebServlet的displayName属性
    <servlet-name>:指定Servlet的名称,一般与Servlet的类名相同,要求在一个web.xml文件内名字唯一,等价于@WebServlet的name属性
    <servlet-class>:指定Servlet类的全限定名,即:包名.类名
    <init-param>:指定Servlet初始化参数,等价于@WebServlet的initParams属性,若有多个参数可重复定义此元素。此元素为可选配置
    <param-name>:指定初始参数名
    <param-value>:指定初始参数名对应的值
    <load-on-startup>:指定Servlet的加载顺序,等价于@WebServlet的loadOnStartup属性
  • web.xml中"<servlet-mapping> </servlet-mapping>”元素用于指定Servlet的URL映射
    <servlet-name>:用来指定要映射的Servlet名称,要与<servlet>声明中的<servlet-name>值一致
    <url-pattern>:指定Servlet的URL匹配模式,等价于@WebServlet的urlPatterns属性或value属性

Servlet生命周期是指Servlet实例从创建到响应客户请求,直至销毁的过程。Servlet程序本身不直接在Java虚拟机上运行,由Servlet容器负责管理其整个生命周期。
Servlet生命周期可分为四个阶段:实例化、初始化、处理请求、销毁。


  • Servlet容器在如下时刻加载和实例化一个Servlet:
    在Servlet容器启动后,客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet 对象的service方法。
    在为Servlet配置了自动装入选项(load-on-startup)时,服务器在启动时会自动装入此Servlet。
  • Servlet初始化
    Servlet实例化后,Servlet容器将调用Servlet的init方法来对Servlet实例进行初始化,初始化成功,Servlet在Web容器中会处于服务可用状态;如果初始化失败,Servlet容器会销毁该实例;当Servlet运行出现异常时,Servlet容器会使该实例变为服务不可用状态。
  • 请求处理
    服务器接收到客户端请求,会为该请求创建“请求”对象和“响应”对象,并调用service()方法,service()方法再调用其他方法来处理请求。
    在Servlet生命周期中,service()方法可能被多次调用。当多个客户端同时访问某个Servlet的service()方法时,服务器会为每个请求创建一个线程,这样可以并行处理多个请求,减少请求处理的等待时间,提高服务器的响应速度。但同时也要注意对同一对象的并发访问问题。
  • 服务终止
    当Servlet容器需要终止Servlet(如Web服务器被关闭或需要出让资源),它会先调用Servlet的destroy()方法使其释放正在使用的资源。
    在调用destroy()方法之前,必须让当前正在执行service()方法的任何线程完成执行,或者超过了服务器定义的时间限制。
    在destroy()方法完成后,Servlet容器必须释放Servlet实例以便被垃圾回收。
二、Servlet基本应用
  • 超链接形式的数据请求语法格式:
    <a href=”URL地址?参数=参数值[&参数=参数值...]”>链接文本</a>
    发送请求的URL地址可以是绝对地址,
    如:http://localhost:8080/ServletDemo1/QueryServlet
    也可以是相对地址,如:QueryServlet、../QueryServlet等形式。
    在开发中大多数使用相对地址,以便于项目的移植。
<a href="UserQueryServlet?name=neuedu&email=neuedu@neuedu.com">查询</a><br>
protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      String name = request.getParameter("name");
      String email = request.getParameter("email");
      response.getWriter().append("name:"+ name + ",email:" + email);
  }
查询结果
  • Form表单数据请求语法格式:
<form action="UserQueryServlet" method = "post">
        用户名:<input type = "text" name = "name">
        邮箱:<input type = "text" name = "email">
        个人爱好:<input type="checkbox" name="love" value="1">吃饭 
                <input type="checkbox" name="love" value="2">睡觉
                <input type="checkbox" name="love" value="3">打豆豆
        <input type = "submit" value = "查询">
</form>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        String name = request.getParameter("name");
        String email = request.getParameter("email");
        //获取复选框的值
        String [] love = request.getParameterValues("love");
    
        response.getWriter().append("name: " + name + ",email:" + email+ ",love:" + love);
                
    }
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
  • 超链接一般用于获取/查询资源信息,属于GET请求类型,请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连。由于其安全性(如:请求数据会以明文显示在地址栏上)以及请求地址的长度限制,一般仅用于传送一些简单的数据;
  • Form表单一般用于更新资源信息,默认使用GET请求类型,多使用POST请求类型。由于POST请求类型理论上没有数据大小限制,可用表单来传较大量的数据;
  • 重定向是指由原请求地址重新定位到某个新地址,原有的request请求失效,客户端看到的是新的request请求返回的响应结果,客户端浏览器地址栏变为新请求地址。


  • 重定向通过HttpServletResponse对象的sendRedirect()方法实现,该方法会通知客户端去重新访问新指定的URL地址,其语法格式如下:
    public void sendRedirect(String location)throws java.io.IOException
    location参数用以指定重定向的URL,它可以是相对路径或绝对路径。
    sendRedirect()方法不仅可以重定向到当前应用程序中的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
@WebServlet("/RedirectServlet")
public class RedirectServlet extends HttpServlet {
       protected void doGet(HttpServletRequest request, >HttpServletResponse         response) throws ServletException, IOException {
      System.out.println("重定向前.....");
      response.sendRedirect("ResultServlet");
      //response.sendRedirect("https://www.baidu.com");
      System.out.println("重定向后.....");
  }
       protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException {
      doGet(request, response);
  }
}
@WebServlet("/ResultServlet")
public class ResultServlet extends HttpServlet {
       protected void doGet(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException {
      // 设置响应到客户端的文本类型为HTML
      response.setContentType("text/html;charset=UTF-8");
      // 获取输出流
      PrintWriter out = response.getWriter();
      // 输出响应结果
      out.println("重定向与请求转发的结果页面");
  }
       protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException {
      doGet(request, response);
  }
}
  • 请求转发是指将请求再转发到其他地址,转发过程中使用的是同一个request请求,转发后浏览器地址栏内容不变。


  • 请求转发的过程发生在服务器内部,只能从当前应用内部查找相应的转发资源,而不能转发到其它应用的资源。
  • 请求转发使用RequestDispatcher接口中的forward()方法来实现,该方法可以把请求转发给另外一个资源,并让该资源对此请求进行响应。
    RequestDispatcher接口有以下两个方法:

forward()方法:将请求转发给其他资源。
include()方法:将其他资源并入到当前请求中。

  • 请求转发语法:
    RequestDispatcher dispatcher = request.getRequestDispatcher(String path);
    dispatcher.forward(ServletRequest request,ServletResponse response);
    其中:
    path参数用以指定转发的URL,只能是相对路径;
    request和response参数取值为当前请求所对应的HttpServletRequest和> > HttpServletResponse对象。
@WebServlet("/ForwardServlet")
public class ForwardServlet extends HttpServlet {
   protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      System.out.println("请求转发前.....");
      request.getRequestDispatcher("ResultServlet").forward(request, >response);
      System.out.println("请求转发后.....");
  }
    protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      doGet(request, response);
  }
}
  • 请求转发与重定向对HttpServletRequest对象属性的存取。
    HttpServletRequest对象属性的存取语法:
    request.setAttribute("attrobj", “value"); // 将attrobj属性值存储到request对象中
    request.getAttribute("attrobj"); // 从request对象中取出attrobj属性值

RedirectServlet中设置msg属性值:

request.setAttribute("msg", "request redirect...");

ForwardServlet中设置msg属性值:

request.setAttribute("msg", "request forward...");

ResultServlet中获取msg属性值:

String msg = (String)request.getAttribute("msg");
PrintWriter out = response.getWriter();
out.println(msg);

结果:


请求转发
重定向
  • 请求转发只能将请求转发给同一个Web应用中的组件;
    而重定向不仅可以重定向到当前应用程序中的其他资源,还可以重定向到同一个站点上的其他应用程序中的资源,或者重定向到其他站点的资源;
  • 重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;
    而请求转发过程结束后,浏览器地址栏保持初始的URL地址不变;
  • 请求转发调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;
    而重定向调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
  • 重定向和请求转发不代表方法的结束,下面的代码还会继续执行,注意在转发或重定向的语句后面不要在编写代码。
三、ServletConfig、ServletContext接口
  • 容器在初始化一个Servlet时,将为该Servlet创建一个唯一的ServletConfig对象,并将这个ServletConfig对象通过init(ServletConfig config)方法传递并保存在此Servlet对象中。
  • 使用ServletConfig接口获取Servlet初始化参数
    通过使用@WebServlet注解的initParams熟悉来配置初始化参数(2.5版本中使用web.xml配置文件方式)
@WebServlet(urlPatterns="/hello",loadOnStartup=1,
      initParams= {@WebInitParam(name="jdbcDriver",value="oracle.jdbc.driver.OracleDriver")
                 , @WebInitParam(name="jdbcUrl",value="thin:2521")})
public class RequestWebInfoServlet extends HttpServlet {   
  //init方法
  @Override
  public void init(ServletConfig config) throws ServletException {
      String jdbcDriver = config.getInitParameter("jdbcDriver");
      String jdbcUrl = config.getInitParameter("jdbcUrl");
      System.out.println("request info Servlet init : jdbcDriver:" + >jdbcDriver  +"," + "jdbcUrl" + jdbcUrl);
  }
}
  • ServletContext也称为Servlet上下文,代表当前Servlet运行环境,是Servlet与Servlet容器之间直接通信的接口;
    Servlet容器在启动一个Web应用时,会为该应用创建一个唯一的ServletContext对象供该应用中的所有Servlet对象共享,Servlet对象可以通过ServletContext对象来访问容器中的各种资源。
  • 获得ServletContext对象的两种方式:
    通过ServletConfig接口的getServletContext()方法获得ServletContext对象;
    通过GenericServlet抽象类的getServletContext()方法获得ServletContext对象,实质上该方法也是调用了ServletConfig的getServletContext()方法。
  • ServletContext接口中提供了以下几种类型的方法:
    获取应用范围的初始化参数的方法;
    存取应用范围域属性的方法;
    获取当前Web应用信息的方法;
    获取当前容器信息和输出日志的方法;
    获取服务器端文件资源的方法。
  • 配置ServletContext初始化参数
<!--配置应用初始化参数(ServletContext) -->
 <context-param>
   <param-name>ctxParam</param-name>
   <param-value>ctxValue</param-value>
 </context-param>
  • servlet文件中获取ServletContext初始化参数
@Override
  public void init(ServletConfig config) throws ServletException {
      ServletContext ctx = config.getServletContext();
      String param = ctx.getInitParameter("ctxParam");
      System.out.println(param);
  }
  • ServletContext对象可以理解为容器内的一个共享空间,可以存放具有应用级别作用域的数据,Web应用中的各个组件都可以共享这些数据。这些共享数据以key/value的形式存放在ServletContext对象中,并以key作为其属性名被访问。
  • 应用域属性的存取方法
    setAttribute(String name,Object object),把一个对象和一个属性名绑定并存放到ServletContext中,参数name指定属性名,参数Object表示共享数据
    getAttribute(String name),根据参数给定的属性名,返回一个Object类型的对象
    getAttributeNames(),返回一个Enumeration对象,该对象包含了所有存放在ServletContext中的属性名。
    removeAttribute(String name),根据参数指定的属性名,从ServletContext对象中删除匹配的属性
  • 页面访问量统计
@WebServlet("/ContextAttributeServlet")
public class ContextAttributeServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
     
  protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // 设置响应到客户端MIME类型和字符编码方式
      response.setContentType("text/html;charset=UTF-8");
      //获取ServletContext对象
      ServletContext ctx = getServletContext();
      //从ctx对象获取count属性存放的计数值
      Integer count = (Integer) ctx.getAttribute("count");
      if (count == null) {//判断是否为第一次访问页面
          count = 1;
      } else {
          count = count + 1;
      }
      //将更新后的数值存放到ServletContext对象的count属性中
      ctx.setAttribute("count", count);
      //获取输出流
      PrintWriter out = response.getWriter();
      //输出计数信息
      out.println("<p>本网站目前访问人数是: " + count + "</p>");
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // TODO Auto-generated method stub
      doGet(request, response);
  }

}

ServletContext对象还包含有关Web应用的信息,
例如:当前Web应用的根路径、应用的名称、应用组件间的转发、以及容器下其他Web应用的ServletContext对象等。

@WebServlet("/ContextAppInfoServlet")
public class ContextAppInfoServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
      
  protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // 设置响应到客户端MIME类型和字符编码方式
      response.setContentType("text/html;charset=UTF-8");
      // 获取当前ServletContext对象
      ServletContext context = super.getServletContext();
      // 获取容器中URL路径为“/HelloServlet”的应用的ServletContext对象
      ServletContext contextByUrl = context.getContext("/HelloServlet");
      // 获取当前Web应用的上下文根路径
      String contextPath = context.getContextPath();
      // 获取当前Web应用的名称
      String contextName = context.getServletContextName();
      // 获取容器中URL路径为“/chapter01”的应用的应用名称
      String contextByUrlName = contextByUrl.getServletContextName();
      // 获取转发请求的RequestDispatcher对象
      RequestDispatcher rd = >context.getRequestDispatcher("/HelloServlet");
      // 获取输出流
      PrintWriter out = response.getWriter();
      out.println("<P>当前Web应用的上下文根路径是:" + contextPath + >"</p>");
      out.println("<p>当前Web应用的名称是:" + contextName + "</p>");
      out.println("<p>容器中URL路径为“/HelloServlet”的应用的应用名称是:" + contextByUrlName);
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // TODO Auto-generated method stub
      doGet(request, response);
  }

}
四、Servlet请求与响应
  • ServletRequest接口被定义为用于封装请求的信息,ServletRequest对象由Servlet容器在用户每次请求Servlet时创建并传入Servlet的service()方法中;
  • HttpServletRequest接口继承了ServletRequest接口,是专用于HTTP协议的子接口,用于封装HTTP请求信息;
  • 在HttpServlet类的service()方法中,传入的ServletRequest对象被强制转换为HttpServletRequest对象来进行HTTP请求信息的处理。
  • HttpServletRequest接口提供了具有如下功能类型的方法:
    获取请求报文信息(包括请求行、请求头、请求正文)的方法;
    获取网络连接信息的方法;
    存取请求域属性的方法。
  • 客户端浏览器和服务器端Servlet通过HTTP协议进行通信,HTTP协议采用请求/响应模型,协议的请求报文由请求行、请求头和可选的请求正文组成。


    HTTP协议的请求报文
  • 请求报文信息格式样例—我们可以通过firefox浏览器的开发者工具查看


  • HTTP协议请求正文内容为POST请求参数名称和值所组成的一个字符,GET请求参数附属在请求行中,没有请求正文。

POST请求乱码问题:

  • 浏览器会按当前显示页面所采用的字符集对请求的中文数据进行编码,再以报文体的形式传送给服务器,Servlet在调用getParameter()方法获取参数时,会以HttpServletRequest对象的getCharacterEncoding()方法返回的字符集对其进行解码,而该方法的返回值在未经过setCharacterEncoding(charset)方法设置编码的情况下为null,这时getParameter()方法将以服务器默认的“ISO-8859-1”字符集对参数进行解码,而“ISO-8859-1”字符集并不包含中文,于是造成中文参数的乱码问题。
  • POST中文乱码解决办法:
    在调用getParameter()方法前先调用setCharacterEncoding(charset)方法设定与页面请求编码相同的解码字符集。
request.setCharacterEncoding("UTF-8");

GET请求的乱码问题:

  • GET请求参数以“?”或“&”为连接字符附加在URL地址后,根据网络标准RFC1738规定,只有字母和数字以及一些特殊符号和某些保留字才可以不经过编码直接用于URL,因此在请求参数为中文时必须先由浏览器进行编码后才能发送给服务器,服务器端对GET请求参数依照服务器本身默认的字符集进行解码。
  • 在服务器端,由于GET请求参数是作为请求行发送给服务器的,因此Servlet在通过getParameter()获取请求参数时,并不能使用setCharacterEncoding(charset)方法指定的字符集进行解码,而是依照服务器本身默认的字符集进行解码。
  • Tomcat服务器各版本中默认的URIEncoding字符集并不完全相同,例如,Tomcat6和Tomcat7都默认为“ISO-8859-1”,这类版本中,对于GET请求的中文参数必须经处理后才会避免乱码问题,因此在实际开发中尽量避免使用GET请求来传递中文参数。
  • 存储在HttpServletRequest对象中的对象称之为请求域属性,属于同一个请求的多个处理组件之间可以通过请求域属性来传递对象数据。
  • HttpServletRequest接口提供了如下与请求域属性相关的方法:
    void setAtrribute(String name,Object value),设定name属性的值为value,保存在request范围内;
    Object getAttribute(String name),从request范围内获取name属性的值;
    void removeAttribute(String name),从request范围内移除name属性的值;
    Enumeration getAttributeNames(),获取所有request范围的属性
  • ServletResponse接口被定义为用于创建响应消息,ServletResponse对象由Servlet容器在用户每次请求Servlet时创建并传入Servlet的service()方法中。
  • HttpServletResponse接口继承自ServletResponse接口,是专用于HTTP协议的子接口,用于封装HTTP响应消息。
  • 在HttpServlet类的service()方法中,传入的ServletResponse对象被强制转换为HttpServletResponse对象来进行HTTP响应信息的处理。
  • HttpServletResponse接口提供了具有如下功能类型的方法:
    设置响应状态的方法;
    构建响应头的方法;
    创建响应正文的方法。
    将服务器的端口号调整为8888,以后的访问路径为:http://localhost:8888/

HTTP协议的响应报文由响应行、响应头和响应正文组成。
HTTP协议响应报文格式如下图:



  • HTTP协议响应报文的响应行由报文协议和版本以及状态码和状态描述构成,状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码可分为如下5种类型:
  • 常见的响应状态码:
    200表示请求成功;
    302表示资源(网页等)暂时转移到其它URL;
    404表示请求的资源(网页等)不存在;
    500表示服务器内部错误。
  • 设置响应消息头
    对于一些常用的消息头, HttpServletResponse提供了一些特定的方法来进行设置:
    setContentType(String mime),设定Content-Type消息头;
    setContentLength(int length),设定Content-Length消息头;
    addHeader(String name,String value),新增String类型的值到名为name的http头部;
    addIntHeader(String name,int value),新增int类型的值到名为name的http头部;
    addDateHeader(String name,long date),新增long类型的值到名为name的http头部;
    addCookie(Cookie c),为Set-Cookie消息头增加一个值;
  • 实现一个页面动态刷新效果
@WebServlet("/ResponseRefreshHeadServlet")
public class ResponseRefreshHeadServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  protected void doGet(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {
      this.doPost(request, response);
  }
  protected void doPost(HttpServletRequest request,
          HttpServletResponse response) throws ServletException, IOException {
      // 设置Content-Type消息头响应文档MIME类型和字符编码方式
      response.setContentType("text/html;charset=UTF-8");
      // 设置响应头“refresh”的值为1秒
      response.setHeader("refresh", "1");
      //获取输出流
      PrintWriter out = response.getWriter();
      out.println("现在时间是:");
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd >hh:mm:ss");
      out.println(sdf.format(new Date()));
  }
  
}
  • 在Servlet中,向客户端输出的响应数据是通过输出流对象来完成的,HttpServletResponse接口提供了两个获取不同类型输出流对象的方法:
    1.getOutputStream(),返回字节输出流对象ServletOutputStream;
    ServletOutputStream对象主要用于输出二进制字节数据。例如,配合setContentType()方法响应输出一个图像、视频等。
    2.getWriter(),返回字符输出流对象PrintWriter;
    PrintWriter对象主要用于输出字符文本内容,但其内部实现仍是将字符串转换成某种字符集编码的字节数组后再进行输出。
  • 当向ServletOutputStream或PrintWriter对象中写入数据后,Servlet容器会将这些数据作为响应消息的正文,然后再与响应状态行和各响应头组合成完整的响应报文输出到客户端,同时,在Serlvet的service()方法结束后,容器还将检查getWriter()或getOutputStream()方法返回的输出流对象是否已经调用过close()方法,如果没有,容器将调用close()方法关闭该输出流对象。
  • 实现响应输出一个图像
@WebServlet("/OutputStreamServlet")
public class OutputStreamServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
 
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              //设置响应消息头Content-Type
              response.setContentType("image/jpeg");
              // 获取ServletContext对象
              ServletContext context = super.getServletContext();
              // 获取读取服务器端文件的输入流
              InputStream is = context.getResourceAsStream("/imgs/logo.png");
              // 获取ServletOutputStream输出流
              ServletOutputStream os = response.getOutputStream();
              int i = 0;
              while ((i = is.read()) != -1) {
                  os.write(i);//向输出流中写入二进制数据
              }
              is.close();
              os.close();

  }
  protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // TODO Auto-generated method stub
      doGet(request, response);
  }
}
  • 由于Java程序中的字符文本在内存中以Unicode编码的形式存在,因此PrintWriter对象在输出字符文本时,需要先将它们转换成其他某种字符集编码的字节数组后输出。
  • PrintWriter对象默认使用ISO-8859-1字符集进行Unicode字符串到字节数组的转换,由于ISO-8859-1字符集中没有中文字符,因此Unicode编码的中文字符将被转换成无效的字符编码后输出给客户端,这就是Servlet中文输出乱码问题的原因。
  • ServletResponse接口中定义了三种方等方法来指定getWriter()方法返回的PrintWriter对象所使用的字符集编码。
    1.response.setCharacterEncoding("UTF-8");
    只能用来设置PrintWriter输出流中字符的编码方式,它的优先权最高。
    2.(推荐使用)response.setContentType("text/html;charset=UTF-8");
    既可以设置PrintWriter输出流中字符的编码方式,也可以设置浏览器接收到这些字符后以什么编码方式来解码,它的优先权低于第一种方法。
    3.response.setLocale(new java.util.Locale("zh","CN"));
    只能用来设置PrintWriter输出流中字符的编码方式,它的优先权最低,在已经使用前两种方法中的一个设置了编码方式以后,它将被覆盖而不再起作用。
上一篇下一篇

猜你喜欢

热点阅读