Servlet技術(shù)

一麸俘、Servlet概述
  • 什么是Servlet

Servlet是基于Java語言的Web服務(wù)器端編程技術(shù)穷躁,按照Java EE規(guī)范定義狞山,Servlet是運行在Servlet容器中的Java類码泞,它能處理Web客戶的HTTP請求吱涉,并產(chǎn)生HTTP響應(yīng)逛薇。


  • Servlet對請求的處理和響應(yīng)過程分為如下幾個步驟:
    接收HTTP請求捺疼;
    取得請求信息,包括請求頭和請求參數(shù)數(shù)據(jù)永罚;
    調(diào)用其他Java類方法啤呼,完成具體的業(yè)務(wù)功能;
    實現(xiàn)到其他Web組件的跳轉(zhuǎn)(包括重定向或請求轉(zhuǎn)發(fā))呢袱;
    生成HTTP響應(yīng)(包括HTML或非HTML響應(yīng))官扣。
  • 創(chuàng)建第一個Servlet

1.創(chuàng)建Java Web項目
2.創(chuàng)建Servlet
3.實現(xiàn)doPost()或doGet()方法
4.聲明配置Servlet:
3.x版本以上規(guī)范中使用注解聲明配置

@WebServlet("/hello")

2.5版本規(guī)范中采用web.xml配置文件聲明配置
5.部署運行Servlet

  • Servlet體系結(jié)構(gòu)

Servlet是使用Servlet API(應(yīng)用程序設(shè)計接口)及相關(guān)類和方法的Java程序。
Servlet API包含兩個軟件包:

  • javax.servlet包
    定義了所有Servlet類都必須實現(xiàn)或繼承的通用接口和類
  • javax.servlet.http包
    定義了采用HTTP協(xié)議通信的HttpServlet類羞福。


    Servlet API的主要接口和類之間的關(guān)系
  • Servlet聲明配置
Servlet聲明配置樣例
  • loadOnStartup
    指定Servlet的加載順序惕蹄。當此選項沒有指定時,表示容器在該Servlet第一次被請求時才加載治专;當值為0或者大于0時卖陵,表示容器在應(yīng)用啟動時就加載這個Servlet。值越小张峰,啟動該servlet的優(yōu)先級越高泪蔫。原則上不同的Servlet應(yīng)該使用不同的啟動順序數(shù)字
  • init(ServletConfig config)
    Servlet初始化方法,在Servlet實例化后挟炬,容器調(diào)用該方法進行Servlet的初始化鸥滨,init()方法只能被調(diào)用一次嗦哆,如果此方法沒有正常結(jié)束,就會拋出一個ServletException異常婿滓,一旦拋出該異常老速,Servlet將不再執(zhí)行,隨后對其進行再次調(diào)用會凸主,容器會重新載入并再次運行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

  • Servlet2.5版本聲明配置
<?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文件內(nèi)名字唯一,等價于@WebServlet的name屬性
    <servlet-class>:指定Servlet類的全限定名衍腥,即:包名.類名
    <init-param>:指定Servlet初始化參數(shù)磺樱,等價于@WebServlet的initParams屬性,若有多個參數(shù)可重復定義此元素婆咸。此元素為可選配置
    <param-name>:指定初始參數(shù)名
    <param-value>:指定初始參數(shù)名對應(yīng)的值
    <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實例從創(chuàng)建到響應(yīng)客戶請求块差,直至銷毀的過程。Servlet程序本身不直接在Java虛擬機上運行倔丈,由Servlet容器負責管理其整個生命周期憨闰。
Servlet生命周期可分為四個階段:實例化、初始化乃沙、處理請求起趾、銷毀。


  • Servlet容器在如下時刻加載和實例化一個Servlet:
    在Servlet容器啟動后警儒,客戶首次向Servlet發(fā)出請求,Servlet容器會判斷內(nèi)存中是否存在指定的Servlet對象眶根,如果沒有則創(chuàng)建它蜀铲,然后根據(jù)客戶的請求創(chuàng)建HttpRequest、HttpResponse對象属百,從而調(diào)用Servlet 對象的service方法记劝。
    在為Servlet配置了自動裝入選項(load-on-startup)時,服務(wù)器在啟動時會自動裝入此Servlet族扰。
  • Servlet初始化
    Servlet實例化后厌丑,Servlet容器將調(diào)用Servlet的init方法來對Servlet實例進行初始化定欧,初始化成功,Servlet在Web容器中會處于服務(wù)可用狀態(tài)怒竿;如果初始化失敗砍鸠,Servlet容器會銷毀該實例;當Servlet運行出現(xiàn)異常時耕驰,Servlet容器會使該實例變?yōu)榉?wù)不可用狀態(tài)爷辱。
  • 請求處理
    服務(wù)器接收到客戶端請求,會為該請求創(chuàng)建“請求”對象和“響應(yīng)”對象朦肘,并調(diào)用service()方法饭弓,service()方法再調(diào)用其他方法來處理請求。
    在Servlet生命周期中媒抠,service()方法可能被多次調(diào)用弟断。當多個客戶端同時訪問某個Servlet的service()方法時,服務(wù)器會為每個請求創(chuàng)建一個線程趴生,這樣可以并行處理多個請求夫嗓,減少請求處理的等待時間,提高服務(wù)器的響應(yīng)速度冲秽。但同時也要注意對同一對象的并發(fā)訪問問題舍咖。
  • 服務(wù)終止
    當Servlet容器需要終止Servlet(如Web服務(wù)器被關(guān)閉或需要出讓資源),它會先調(diào)用Servlet的destroy()方法使其釋放正在使用的資源锉桑。
    在調(diào)用destroy()方法之前排霉,必須讓當前正在執(zhí)行service()方法的任何線程完成執(zhí)行,或者超過了服務(wù)器定義的時間限制民轴。
    在destroy()方法完成后攻柠,Servlet容器必須釋放Servlet實例以便被垃圾回收。
二后裸、Servlet基本應(yīng)用
  • 處理超鏈接請求數(shù)據(jù)
  • 超鏈接形式的數(shù)據(jù)請求語法格式:
    <a href=”URL地址?參數(shù)=參數(shù)值[&參數(shù)=參數(shù)值...]”>鏈接文本</a>
    發(fā)送請求的URL地址可以是絕對地址瑰钮,
    如:http://localhost:8080/ServletDemo1/QueryServlet
    也可以是相對地址微驶,如:QueryServlet浪谴、../QueryServlet等形式。
    在開發(fā)中大多數(shù)使用相對地址因苹,以便于項目的移植苟耻。
<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);
  }
查詢結(jié)果
  • 處理Form表單請求數(shù)據(jù)
  • Form表單數(shù)據(jù)請求語法格式:
<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請求類型扶檐,請求的數(shù)據(jù)會附在URL之后凶杖,以?分割URL和傳輸數(shù)據(jù),參數(shù)之間以&相連款筑。由于其安全性(如:請求數(shù)據(jù)會以明文顯示在地址欄上)以及請求地址的長度限制智蝠,一般僅用于傳送一些簡單的數(shù)據(jù)腾么;
  • Form表單一般用于更新資源信息,默認使用GET請求類型杈湾,多使用POST請求類型解虱。由于POST請求類型理論上沒有數(shù)據(jù)大小限制,可用表單來傳較大量的數(shù)據(jù)毛秘;
  • 重定向
  • 重定向是指由原請求地址重新定位到某個新地址饭寺,原有的request請求失效,客戶端看到的是新的request請求返回的響應(yīng)結(jié)果叫挟,客戶端瀏覽器地址欄變?yōu)樾抡埱蟮刂贰?/p>

  • 重定向通過HttpServletResponse對象的sendRedirect()方法實現(xiàn)艰匙,該方法會通知客戶端去重新訪問新指定的URL地址,其語法格式如下:
    public void sendRedirect(String location)throws java.io.IOException
    location參數(shù)用以指定重定向的URL抹恳,它可以是相對路徑或絕對路徑员凝。
    sendRedirect()方法不僅可以重定向到當前應(yīng)用程序中的其他資源,還可以重定向到同一個站點上的其他應(yīng)用程序中的資源奋献,甚至是使用絕對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 {
      // 設(shè)置響應(yīng)到客戶端的文本類型為HTML
      response.setContentType("text/html;charset=UTF-8");
      // 獲取輸出流
      PrintWriter out = response.getWriter();
      // 輸出響應(yīng)結(jié)果
      out.println("重定向與請求轉(zhuǎn)發(fā)的結(jié)果頁面");
  }
       protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException {
      doGet(request, response);
  }
}
  • 請求轉(zhuǎn)發(fā)
  • 請求轉(zhuǎn)發(fā)是指將請求再轉(zhuǎn)發(fā)到其他地址,轉(zhuǎn)發(fā)過程中使用的是同一個request請求瓶蚂,轉(zhuǎn)發(fā)后瀏覽器地址欄內(nèi)容不變糖埋。


  • 請求轉(zhuǎn)發(fā)的過程發(fā)生在服務(wù)器內(nèi)部,只能從當前應(yīng)用內(nèi)部查找相應(yīng)的轉(zhuǎn)發(fā)資源窃这,而不能轉(zhuǎn)發(fā)到其它應(yīng)用的資源瞳别。
  • 請求轉(zhuǎn)發(fā)使用RequestDispatcher接口中的forward()方法來實現(xiàn),該方法可以把請求轉(zhuǎn)發(fā)給另外一個資源杭攻,并讓該資源對此請求進行響應(yīng)祟敛。
    RequestDispatcher接口有以下兩個方法:

forward()方法:將請求轉(zhuǎn)發(fā)給其他資源。
include()方法:將其他資源并入到當前請求中兆解。

  • 請求轉(zhuǎn)發(fā)語法:
    RequestDispatcher dispatcher = request.getRequestDispatcher(String path);
    dispatcher.forward(ServletRequest request,ServletResponse response);
    其中:
    path參數(shù)用以指定轉(zhuǎn)發(fā)的URL馆铁,只能是相對路徑;
    request和response參數(shù)取值為當前請求所對應(yīng)的HttpServletRequest和> > HttpServletResponse對象锅睛。
@WebServlet("/ForwardServlet")
public class ForwardServlet extends HttpServlet {
   protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      System.out.println("請求轉(zhuǎn)發(fā)前.....");
      request.getRequestDispatcher("ResultServlet").forward(request, >response);
      System.out.println("請求轉(zhuǎn)發(fā)后.....");
  }
    protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      doGet(request, response);
  }
}
  • 請求轉(zhuǎn)發(fā)與重定向數(shù)據(jù)傳遞
  • 請求轉(zhuǎn)發(fā)與重定向?qū)ttpServletRequest對象屬性的存取埠巨。
    HttpServletRequest對象屬性的存取語法:
    request.setAttribute("attrobj", “value"); // 將attrobj屬性值存儲到request對象中
    request.getAttribute("attrobj"); // 從request對象中取出attrobj屬性值

RedirectServlet中設(shè)置msg屬性值:

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

ForwardServlet中設(shè)置msg屬性值:

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

ResultServlet中獲取msg屬性值:

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

結(jié)果:


請求轉(zhuǎn)發(fā)

重定向
  • 請求轉(zhuǎn)發(fā)與重定向的區(qū)別
    重定向和請求轉(zhuǎn)發(fā)都可以讓瀏覽器獲得另外一個URL所指向的資源,但兩者的內(nèi)部運行機制有很大的區(qū)別:
  • 請求轉(zhuǎn)發(fā)只能將請求轉(zhuǎn)發(fā)給同一個Web應(yīng)用中的組件衣撬;
    而重定向不僅可以重定向到當前應(yīng)用程序中的其他資源乖订,還可以重定向到同一個站點上的其他應(yīng)用程序中的資源,或者重定向到其他站點的資源具练;
  • 重定向的訪問過程結(jié)束后,瀏覽器地址欄中顯示的URL會發(fā)生改變甜无,由初始的URL地址變成重定向的目標URL扛点;
    而請求轉(zhuǎn)發(fā)過程結(jié)束后哥遮,瀏覽器地址欄保持初始的URL地址不變;
  • 請求轉(zhuǎn)發(fā)調(diào)用者與被調(diào)用者之間共享相同的request對象和response對象陵究,它們屬于同一個訪問請求和響應(yīng)過程眠饮;
    而重定向調(diào)用者與被調(diào)用者使用各自的request對象和response對象,它們屬于兩個獨立的訪問請求和響應(yīng)過程铜邮。
  • 重定向和請求轉(zhuǎn)發(fā)不代表方法的結(jié)束仪召,下面的代碼還會繼續(xù)執(zhí)行,注意在轉(zhuǎn)發(fā)或重定向的語句后面不要在編寫代碼松蒜。
三扔茅、ServletConfig、ServletContext接口
  • ServletConfig接口
  • 容器在初始化一個Servlet時秸苗,將為該Servlet創(chuàng)建一個唯一的ServletConfig對象召娜,并將這個ServletConfig對象通過init(ServletConfig config)方法傳遞并保存在此Servlet對象中。
  • 使用ServletConfig接口獲取Servlet初始化參數(shù)
    通過使用@WebServlet注解的initParams熟悉來配置初始化參數(shù)(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接口
  • ServletContext也稱為Servlet上下文惊楼,代表當前Servlet運行環(huán)境玖瘸,是Servlet與Servlet容器之間直接通信的接口;
    Servlet容器在啟動一個Web應(yīng)用時檀咙,會為該應(yīng)用創(chuàng)建一個唯一的ServletContext對象供該應(yīng)用中的所有Servlet對象共享雅倒,Servlet對象可以通過ServletContext對象來訪問容器中的各種資源。
  • 獲得ServletContext對象的兩種方式:
    通過ServletConfig接口的getServletContext()方法獲得ServletContext對象弧可;
    通過GenericServlet抽象類的getServletContext()方法獲得ServletContext對象蔑匣,實質(zhì)上該方法也是調(diào)用了ServletConfig的getServletContext()方法。
  • ServletContext接口中提供了以下幾種類型的方法:
    獲取應(yīng)用范圍的初始化參數(shù)的方法侣诺;
    存取應(yīng)用范圍域?qū)傩缘姆椒ǎ?br> 獲取當前Web應(yīng)用信息的方法殖演;
    獲取當前容器信息和輸出日志的方法;
    獲取服務(wù)器端文件資源的方法年鸳。
  • 配置ServletContext初始化參數(shù)
<!--配置應(yīng)用初始化參數(shù)(ServletContext) -->
 <context-param>
   <param-name>ctxParam</param-name>
   <param-value>ctxValue</param-value>
 </context-param>
  • servlet文件中獲取ServletContext初始化參數(shù)
@Override
  public void init(ServletConfig config) throws ServletException {
      ServletContext ctx = config.getServletContext();
      String param = ctx.getInitParameter("ctxParam");
      System.out.println(param);
  }
  • ServletContext域?qū)傩?/li>
  • ServletContext對象可以理解為容器內(nèi)的一個共享空間趴久,可以存放具有應(yīng)用級別作用域的數(shù)據(jù),Web應(yīng)用中的各個組件都可以共享這些數(shù)據(jù)搔确。這些共享數(shù)據(jù)以key/value的形式存放在ServletContext對象中彼棍,并以key作為其屬性名被訪問。
  • 應(yīng)用域?qū)傩缘拇嫒》椒?br> setAttribute(String name,Object object)膳算,把一個對象和一個屬性名綁定并存放到ServletContext中座硕,參數(shù)name指定屬性名,參數(shù)Object表示共享數(shù)據(jù)
    getAttribute(String name)涕蜂,根據(jù)參數(shù)給定的屬性名华匾,返回一個Object類型的對象
    getAttributeNames(),返回一個Enumeration對象机隙,該對象包含了所有存放在ServletContext中的屬性名蜘拉。
    removeAttribute(String name)萨西,根據(jù)參數(shù)指定的屬性名,從ServletContext對象中刪除匹配的屬性
  • 頁面訪問量統(tǒng)計
@WebServlet("/ContextAttributeServlet")
public class ContextAttributeServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
     
  protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // 設(shè)置響應(yīng)到客戶端MIME類型和字符編碼方式
      response.setContentType("text/html;charset=UTF-8");
      //獲取ServletContext對象
      ServletContext ctx = getServletContext();
      //從ctx對象獲取count屬性存放的計數(shù)值
      Integer count = (Integer) ctx.getAttribute("count");
      if (count == null) {//判斷是否為第一次訪問頁面
          count = 1;
      } else {
          count = count + 1;
      }
      //將更新后的數(shù)值存放到ServletContext對象的count屬性中
      ctx.setAttribute("count", count);
      //獲取輸出流
      PrintWriter out = response.getWriter();
      //輸出計數(shù)信息
      out.println("<p>本網(wǎng)站目前訪問人數(shù)是: " + count + "</p>");
  }

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

}
  • 獲取Web應(yīng)用信息

ServletContext對象還包含有關(guān)Web應(yīng)用的信息旭旭,
例如:當前Web應(yīng)用的根路徑谎脯、應(yīng)用的名稱、應(yīng)用組件間的轉(zhuǎn)發(fā)持寄、以及容器下其他Web應(yīng)用的ServletContext對象等源梭。

@WebServlet("/ContextAppInfoServlet")
public class ContextAppInfoServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
      
  protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // 設(shè)置響應(yīng)到客戶端MIME類型和字符編碼方式
      response.setContentType("text/html;charset=UTF-8");
      // 獲取當前ServletContext對象
      ServletContext context = super.getServletContext();
      // 獲取容器中URL路徑為“/HelloServlet”的應(yīng)用的ServletContext對象
      ServletContext contextByUrl = context.getContext("/HelloServlet");
      // 獲取當前Web應(yīng)用的上下文根路徑
      String contextPath = context.getContextPath();
      // 獲取當前Web應(yīng)用的名稱
      String contextName = context.getServletContextName();
      // 獲取容器中URL路徑為“/chapter01”的應(yīng)用的應(yīng)用名稱
      String contextByUrlName = contextByUrl.getServletContextName();
      // 獲取轉(zhuǎn)發(fā)請求的RequestDispatcher對象
      RequestDispatcher rd = >context.getRequestDispatcher("/HelloServlet");
      // 獲取輸出流
      PrintWriter out = response.getWriter();
      out.println("<P>當前Web應(yīng)用的上下文根路徑是:" + contextPath + >"</p>");
      out.println("<p>當前Web應(yīng)用的名稱是:" + contextName + "</p>");
      out.println("<p>容器中URL路徑為“/HelloServlet”的應(yīng)用的應(yīng)用名稱是:" + contextByUrlName);
  }

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

}
四、Servlet請求與響應(yīng)
  • HttpServletRequest接口
  • ServletRequest接口被定義為用于封裝請求的信息稍味,ServletRequest對象由Servlet容器在用戶每次請求Servlet時創(chuàng)建并傳入Servlet的service()方法中废麻;
  • HttpServletRequest接口繼承了ServletRequest接口,是專用于HTTP協(xié)議的子接口仲闽,用于封裝HTTP請求信息脑溢;
  • 在HttpServlet類的service()方法中,傳入的ServletRequest對象被強制轉(zhuǎn)換為HttpServletRequest對象來進行HTTP請求信息的處理赖欣。
  • HttpServletRequest接口提供了具有如下功能類型的方法:
    獲取請求報文信息(包括請求行屑彻、請求頭、請求正文)的方法顶吮;
    獲取網(wǎng)絡(luò)連接信息的方法社牲;
    存取請求域?qū)傩缘姆椒ā?/li>
  • HTTP協(xié)議
  • 客戶端瀏覽器和服務(wù)器端Servlet通過HTTP協(xié)議進行通信,HTTP協(xié)議采用請求/響應(yīng)模型悴了,協(xié)議的請求報文由請求行搏恤、請求頭和可選的請求正文組成。


    HTTP協(xié)議的請求報文
  • 請求報文信息格式樣例—我們可以通過firefox瀏覽器的開發(fā)者工具查看


  • HTTP協(xié)議請求正文內(nèi)容為POST請求參數(shù)名稱和值所組成的一個字符湃交,GET請求參數(shù)附屬在請求行中熟空,沒有請求正文。
  • HTTP請求參數(shù)中文亂碼問題
    在進行請求參數(shù)傳遞時搞莺,經(jīng)常會遇到請求數(shù)據(jù)為中文時的亂碼問題息罗,出現(xiàn)亂碼的原因與客戶端的請求編碼方式(GET請求或POST請求)以及服務(wù)器的處理編碼方式有關(guān)。

POST請求亂碼問題:

  • 瀏覽器會按當前顯示頁面所采用的字符集對請求的中文數(shù)據(jù)進行編碼才沧,再以報文體的形式傳送給服務(wù)器迈喉,Servlet在調(diào)用getParameter()方法獲取參數(shù)時,會以HttpServletRequest對象的getCharacterEncoding()方法返回的字符集對其進行解碼温圆,而該方法的返回值在未經(jīng)過setCharacterEncoding(charset)方法設(shè)置編碼的情況下為null挨摸,這時getParameter()方法將以服務(wù)器默認的“ISO-8859-1”字符集對參數(shù)進行解碼,而“ISO-8859-1”字符集并不包含中文岁歉,于是造成中文參數(shù)的亂碼問題得运。
  • POST中文亂碼解決辦法:
    在調(diào)用getParameter()方法前先調(diào)用setCharacterEncoding(charset)方法設(shè)定與頁面請求編碼相同的解碼字符集。
request.setCharacterEncoding("UTF-8");

GET請求的亂碼問題:

  • GET請求參數(shù)以“?”或“&”為連接字符附加在URL地址后,根據(jù)網(wǎng)絡(luò)標準RFC1738規(guī)定澈圈,只有字母和數(shù)字以及一些特殊符號和某些保留字才可以不經(jīng)過編碼直接用于URL彬檀,因此在請求參數(shù)為中文時必須先由瀏覽器進行編碼后才能發(fā)送給服務(wù)器帆啃,服務(wù)器端對GET請求參數(shù)依照服務(wù)器本身默認的字符集進行解碼瞬女。
  • 在服務(wù)器端,由于GET請求參數(shù)是作為請求行發(fā)送給服務(wù)器的努潘,因此Servlet在通過getParameter()獲取請求參數(shù)時诽偷,并不能使用setCharacterEncoding(charset)方法指定的字符集進行解碼,而是依照服務(wù)器本身默認的字符集進行解碼疯坤。
  • Tomcat服務(wù)器各版本中默認的URIEncoding字符集并不完全相同报慕,例如,Tomcat6和Tomcat7都默認為“ISO-8859-1”压怠,這類版本中眠冈,對于GET請求的中文參數(shù)必須經(jīng)處理后才會避免亂碼問題,因此在實際開發(fā)中盡量避免使用GET請求來傳遞中文參數(shù)菌瘫。
  • HttpServletRequest屬性
  • 存儲在HttpServletRequest對象中的對象稱之為請求域?qū)傩晕贤纾瑢儆谕粋€請求的多個處理組件之間可以通過請求域?qū)傩詠韨鬟f對象數(shù)據(jù)。
  • HttpServletRequest接口提供了如下與請求域?qū)傩韵嚓P(guān)的方法:
    void setAtrribute(String name,Object value)雨让,設(shè)定name屬性的值為value,保存在request范圍內(nèi)雇盖;
    Object getAttribute(String name),從request范圍內(nèi)獲取name屬性的值栖忠;
    void removeAttribute(String name)崔挖,從request范圍內(nèi)移除name屬性的值;
    Enumeration getAttributeNames()庵寞,獲取所有request范圍的屬性
  • HttpServletResponse接口
  • ServletResponse接口被定義為用于創(chuàng)建響應(yīng)消息狸相,ServletResponse對象由Servlet容器在用戶每次請求Servlet時創(chuàng)建并傳入Servlet的service()方法中。
  • HttpServletResponse接口繼承自ServletResponse接口捐川,是專用于HTTP協(xié)議的子接口脓鹃,用于封裝HTTP響應(yīng)消息。
  • 在HttpServlet類的service()方法中属拾,傳入的ServletResponse對象被強制轉(zhuǎn)換為HttpServletResponse對象來進行HTTP響應(yīng)信息的處理将谊。
  • HttpServletResponse接口提供了具有如下功能類型的方法:
    設(shè)置響應(yīng)狀態(tài)的方法;
    構(gòu)建響應(yīng)頭的方法渐白;
    創(chuàng)建響應(yīng)正文的方法尊浓。
    將服務(wù)器的端口號調(diào)整為8888,以后的訪問路徑為:http://localhost:8888/
  • HTTP協(xié)議響應(yīng)報文

HTTP協(xié)議的響應(yīng)報文由響應(yīng)行纯衍、響應(yīng)頭和響應(yīng)正文組成栋齿。
HTTP協(xié)議響應(yīng)報文格式如下圖:



  • HTTP協(xié)議響應(yīng)行
  • HTTP協(xié)議響應(yīng)報文的響應(yīng)行由報文協(xié)議和版本以及狀態(tài)碼和狀態(tài)描述構(gòu)成,狀態(tài)碼由三個十進制數(shù)字組成,第一個十進制數(shù)字定義了狀態(tài)碼的類型瓦堵,后兩個數(shù)字沒有分類的作用基协。HTTP狀態(tài)碼可分為如下5種類型:
  • 常見的響應(yīng)狀態(tài)碼:
    200表示請求成功;
    302表示資源(網(wǎng)頁等)暫時轉(zhuǎn)移到其它URL菇用;
    404表示請求的資源(網(wǎng)頁等)不存在澜驮;
    500表示服務(wù)器內(nèi)部錯誤。
  • HTTP協(xié)議響應(yīng)消息頭
  • 設(shè)置響應(yīng)消息頭
    對于一些常用的消息頭惋鸥, HttpServletResponse提供了一些特定的方法來進行設(shè)置:
    setContentType(String mime)杂穷,設(shè)定Content-Type消息頭;
    setContentLength(int length)卦绣,設(shè)定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消息頭增加一個值姑曙;
  • 實現(xiàn)一個頁面動態(tài)刷新效果
@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 {
      // 設(shè)置Content-Type消息頭響應(yīng)文檔MIME類型和字符編碼方式
      response.setContentType("text/html;charset=UTF-8");
      // 設(shè)置響應(yīng)頭“refresh”的值為1秒
      response.setHeader("refresh", "1");
      //獲取輸出流
      PrintWriter out = response.getWriter();
      out.println("現(xiàn)在時間是:");
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd >hh:mm:ss");
      out.println(sdf.format(new Date()));
  }
  
}
  • 創(chuàng)建響應(yīng)正文
  • 在Servlet中,向客戶端輸出的響應(yīng)數(shù)據(jù)是通過輸出流對象來完成的趋厉,HttpServletResponse接口提供了兩個獲取不同類型輸出流對象的方法:
    1.getOutputStream()缝龄,返回字節(jié)輸出流對象ServletOutputStream汰现;
    ServletOutputStream對象主要用于輸出二進制字節(jié)數(shù)據(jù)。例如叔壤,配合setContentType()方法響應(yīng)輸出一個圖像瞎饲、視頻等。
    2.getWriter()炼绘,返回字符輸出流對象PrintWriter嗅战;
    PrintWriter對象主要用于輸出字符文本內(nèi)容,但其內(nèi)部實現(xiàn)仍是將字符串轉(zhuǎn)換成某種字符集編碼的字節(jié)數(shù)組后再進行輸出俺亮。
  • 當向ServletOutputStream或PrintWriter對象中寫入數(shù)據(jù)后驮捍,Servlet容器會將這些數(shù)據(jù)作為響應(yīng)消息的正文,然后再與響應(yīng)狀態(tài)行和各響應(yīng)頭組合成完整的響應(yīng)報文輸出到客戶端脚曾,同時东且,在Serlvet的service()方法結(jié)束后,容器還將檢查getWriter()或getOutputStream()方法返回的輸出流對象是否已經(jīng)調(diào)用過close()方法本讥,如果沒有珊泳,容器將調(diào)用close()方法關(guān)閉該輸出流對象鲁冯。
  • 實現(xiàn)響應(yīng)輸出一個圖像
@WebServlet("/OutputStreamServlet")
public class OutputStreamServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
 
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              //設(shè)置響應(yīng)消息頭Content-Type
              response.setContentType("image/jpeg");
              // 獲取ServletContext對象
              ServletContext context = super.getServletContext();
              // 獲取讀取服務(wù)器端文件的輸入流
              InputStream is = context.getResourceAsStream("/imgs/logo.png");
              // 獲取ServletOutputStream輸出流
              ServletOutputStream os = response.getOutputStream();
              int i = 0;
              while ((i = is.read()) != -1) {
                  os.write(i);//向輸出流中寫入二進制數(shù)據(jù)
              }
              is.close();
              os.close();

  }
  protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException {
      // TODO Auto-generated method stub
      doGet(request, response);
  }
}
  • 響應(yīng)輸出中文亂碼問題
  • 由于Java程序中的字符文本在內(nèi)存中以Unicode編碼的形式存在,因此PrintWriter對象在輸出字符文本時色查,需要先將它們轉(zhuǎn)換成其他某種字符集編碼的字節(jié)數(shù)組后輸出薯演。
  • PrintWriter對象默認使用ISO-8859-1字符集進行Unicode字符串到字節(jié)數(shù)組的轉(zhuǎn)換,由于ISO-8859-1字符集中沒有中文字符秧了,因此Unicode編碼的中文字符將被轉(zhuǎn)換成無效的字符編碼后輸出給客戶端跨扮,這就是Servlet中文輸出亂碼問題的原因。
  • ServletResponse接口中定義了三種方等方法來指定getWriter()方法返回的PrintWriter對象所使用的字符集編碼示惊。
    1.response.setCharacterEncoding("UTF-8");
    只能用來設(shè)置PrintWriter輸出流中字符的編碼方式好港,它的優(yōu)先權(quán)最高。
    2.(推薦使用)response.setContentType("text/html;charset=UTF-8");
    既可以設(shè)置PrintWriter輸出流中字符的編碼方式米罚,也可以設(shè)置瀏覽器接收到這些字符后以什么編碼方式來解碼,它的優(yōu)先權(quán)低于第一種方法丈探。
    3.response.setLocale(new java.util.Locale("zh","CN"));
    只能用來設(shè)置PrintWriter輸出流中字符的編碼方式录择,它的優(yōu)先權(quán)最低,在已經(jīng)使用前兩種方法中的一個設(shè)置了編碼方式以后碗降,它將被覆蓋而不再起作用隘竭。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市讼渊,隨后出現(xiàn)的幾起案子动看,更是在濱河造成了極大的恐慌,老刑警劉巖爪幻,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菱皆,死亡現(xiàn)場離奇詭異,居然都是意外死亡挨稿,警方通過查閱死者的電腦和手機仇轻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奶甘,“玉大人篷店,你說我怎么就攤上這事〕艏遥” “怎么了疲陕?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钉赁。 經(jīng)常有香客問我蹄殃,道長,這世上最難降的妖魔是什么橄霉? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任窃爷,我火速辦了婚禮邑蒋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘按厘。我一直安慰自己医吊,他們只是感情好逮京,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布懒棉。 她就那樣靜靜地躺著草描,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穗慕。 梳的紋絲不亂的頭發(fā)上妻导,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天逛绵,我揣著相機與錄音,去河邊找鬼倔韭。 笑死,一個胖子當著我的面吹牛胰苏,可吹牛的內(nèi)容都是我干的醇疼。 我是一名探鬼主播僵腺,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼普监!你這毒婦竟也來了凯正?” 一聲冷哼從身側(cè)響起豌蟋,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤梧疲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缭受,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡韭畸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年胰丁,在試婚紗的時候發(fā)現(xiàn)自己被綠了喂分。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妻顶。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡讳嘱,死狀恐怖酿愧,靈堂內(nèi)的尸體忽然破棺而出嬉挡,到底是詐尸還是另有隱情,我是刑警寧澤拔恰,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布颜懊,位于F島的核電站风皿,受9級特大地震影響桐款,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜媳维,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一侄刽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧袋励,春花似錦茬故、人聲如沸蚁鳖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至间螟,卻和暖如春损肛,著一層夾襖步出監(jiān)牢的瞬間治拿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鳄梅,地道東北人未檩。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓冤狡,卻偏偏與公主長得像项棠,于是被迫代替她去往敵國和親香追。 傳聞我的和親對象是個殘疾皇子透典,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容