Servlet技術(shù)

一、Servlet概述
  • 什么是Servlet

Servlet是基于Java語(yǔ)言的Web服務(wù)器端編程技術(shù),按照J(rèn)ava EE規(guī)范定義,Servlet是運(yùn)行在Servlet容器中的Java類(lèi),它能處理Web客戶的HTTP請(qǐng)求,并產(chǎn)生HTTP響應(yīng)。


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

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

@WebServlet("/hello")

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

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

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

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


    Servlet API的主要接口和類(lèi)之間的關(guān)系
  • Servlet聲明配置
Servlet聲明配置樣例
  • loadOnStartup
    指定Servlet的加載順序。當(dāng)此選項(xiàng)沒(méi)有指定時(shí),表示容器在該Servlet第一次被請(qǐng)求時(shí)才加載;當(dāng)值為0或者大于0時(shí),表示容器在應(yīng)用啟動(dòng)時(shí)就加載這個(gè)Servlet。值越小,啟動(dòng)該servlet的優(yōu)先級(jí)越高。原則上不同的Servlet應(yīng)該使用不同的啟動(dòng)順序數(shù)字
  • init(ServletConfig config)
    Servlet初始化方法,在Servlet實(shí)例化后,容器調(diào)用該方法進(jìn)行Servlet的初始化,init()方法只能被調(diào)用一次,如果此方法沒(méi)有正常結(jié)束,就會(huì)拋出一個(gè)ServletException異常,一旦拋出該異常,Servlet將不再執(zhí)行,隨后對(duì)其進(jìn)行再次調(diào)用會(huì),容器會(huì)重新載入并再次運(yùn)行init()方法。
  • Servlet的映射路徑
urlPatterns= {"/hello","/hello.do","/hello/hello.html"}
urlPatterns= {"/hello","*.do"}

1.精確匹配:
/hello
訪問(wèn)路徑:http://localhost:8080/HelloServlet/hello
/hello/hello.do
訪問(wèn)路徑:http://localhost:8080/HelloServlet/hello/hello.do
2.模糊匹配:
/*
訪問(wèn)路徑:http://localhost:8080/HelloServlet/任意路徑
/hello/*
訪問(wèn)路徑: http://localhost:8080/HelloServlet/hello/任意路徑
.do、.action、*.后綴名
訪問(wèn)路徑: 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中通過(guò)“<servlet> </servlet>”元素聲明Servlet
    <display-name>:指定該Servlet的顯示名,通常配合工具使用,等價(jià)于@WebServlet的displayName屬性
    <servlet-name>:指定Servlet的名稱(chēng),一般與Servlet的類(lèi)名相同,要求在一個(gè)web.xml文件內(nèi)名字唯一,等價(jià)于@WebServlet的name屬性
    <servlet-class>:指定Servlet類(lèi)的全限定名,即:包名.類(lèi)名
    <init-param>:指定Servlet初始化參數(shù),等價(jià)于@WebServlet的initParams屬性,若有多個(gè)參數(shù)可重復(fù)定義此元素。此元素為可選配置
    <param-name>:指定初始參數(shù)名
    <param-value>:指定初始參數(shù)名對(duì)應(yīng)的值
    <load-on-startup>:指定Servlet的加載順序,等價(jià)于@WebServlet的loadOnStartup屬性
  • web.xml中"<servlet-mapping> </servlet-mapping>”元素用于指定Servlet的URL映射
    <servlet-name>:用來(lái)指定要映射的Servlet名稱(chēng),要與<servlet>聲明中的<servlet-name>值一致
    <url-pattern>:指定Servlet的URL匹配模式,等價(jià)于@WebServlet的urlPatterns屬性或value屬性
  • Servlet生命周期

Servlet生命周期是指Servlet實(shí)例從創(chuàng)建到響應(yīng)客戶請(qǐng)求,直至銷(xiāo)毀的過(guò)程。Servlet程序本身不直接在Java虛擬機(jī)上運(yùn)行,由Servlet容器負(fù)責(zé)管理其整個(gè)生命周期。
Servlet生命周期可分為四個(gè)階段:實(shí)例化、初始化、處理請(qǐng)求、銷(xiāo)毀。


  • Servlet容器在如下時(shí)刻加載和實(shí)例化一個(gè)Servlet:
    在Servlet容器啟動(dòng)后,客戶首次向Servlet發(fā)出請(qǐng)求,Servlet容器會(huì)判斷內(nèi)存中是否存在指定的Servlet對(duì)象,如果沒(méi)有則創(chuàng)建它,然后根據(jù)客戶的請(qǐng)求創(chuàng)建HttpRequest、HttpResponse對(duì)象,從而調(diào)用Servlet 對(duì)象的service方法。
    在為Servlet配置了自動(dòng)裝入選項(xiàng)(load-on-startup)時(shí),服務(wù)器在啟動(dòng)時(shí)會(huì)自動(dòng)裝入此Servlet。
  • Servlet初始化
    Servlet實(shí)例化后,Servlet容器將調(diào)用Servlet的init方法來(lái)對(duì)Servlet實(shí)例進(jìn)行初始化,初始化成功,Servlet在Web容器中會(huì)處于服務(wù)可用狀態(tài);如果初始化失敗,Servlet容器會(huì)銷(xiāo)毀該實(shí)例;當(dāng)Servlet運(yùn)行出現(xiàn)異常時(shí),Servlet容器會(huì)使該實(shí)例變?yōu)榉?wù)不可用狀態(tài)。
  • 請(qǐng)求處理
    服務(wù)器接收到客戶端請(qǐng)求,會(huì)為該請(qǐng)求創(chuàng)建“請(qǐng)求”對(duì)象和“響應(yīng)”對(duì)象,并調(diào)用service()方法,service()方法再調(diào)用其他方法來(lái)處理請(qǐng)求。
    在Servlet生命周期中,service()方法可能被多次調(diào)用。當(dāng)多個(gè)客戶端同時(shí)訪問(wèn)某個(gè)Servlet的service()方法時(shí),服務(wù)器會(huì)為每個(gè)請(qǐng)求創(chuàng)建一個(gè)線程,這樣可以并行處理多個(gè)請(qǐng)求,減少請(qǐng)求處理的等待時(shí)間,提高服務(wù)器的響應(yīng)速度。但同時(shí)也要注意對(duì)同一對(duì)象的并發(fā)訪問(wèn)問(wèn)題。
  • 服務(wù)終止
    當(dāng)Servlet容器需要終止Servlet(如Web服務(wù)器被關(guān)閉或需要出讓資源),它會(huì)先調(diào)用Servlet的destroy()方法使其釋放正在使用的資源。
    在調(diào)用destroy()方法之前,必須讓當(dāng)前正在執(zhí)行service()方法的任何線程完成執(zhí)行,或者超過(guò)了服務(wù)器定義的時(shí)間限制。
    在destroy()方法完成后,Servlet容器必須釋放Servlet實(shí)例以便被垃圾回收。
二、Servlet基本應(yīng)用
  • 處理超鏈接請(qǐng)求數(shù)據(jù)
  • 超鏈接形式的數(shù)據(jù)請(qǐng)求語(yǔ)法格式:
    <a href=”URL地址?參數(shù)=參數(shù)值[&參數(shù)=參數(shù)值...]”>鏈接文本</a>
    發(fā)送請(qǐng)求的URL地址可以是絕對(duì)地址,
    如:http://localhost:8080/ServletDemo1/QueryServlet
    也可以是相對(duì)地址,如:QueryServlet、../QueryServlet等形式。
    在開(kāi)發(fā)中大多數(shù)使用相對(duì)地址,以便于項(xiàng)目的移植。
<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表單請(qǐng)求數(shù)據(jù)
  • Form表單數(shù)據(jù)請(qǐng)求語(yǔ)法格式:
<form action="UserQueryServlet" method = "post">
        用戶名:<input type = "text" name = "name">
        郵箱:<input type = "text" name = "email">
        個(gè)人愛(ài)好:<input type="checkbox" name="love" value="1">吃飯 
                <input type="checkbox" name="love" value="2">睡覺(jué)
                <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");
        //獲取復(fù)選框的值
        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請(qǐng)求類(lèi)型,請(qǐng)求的數(shù)據(jù)會(huì)附在URL之后,以?分割URL和傳輸數(shù)據(jù),參數(shù)之間以&相連。由于其安全性(如:請(qǐng)求數(shù)據(jù)會(huì)以明文顯示在地址欄上)以及請(qǐng)求地址的長(zhǎng)度限制,一般僅用于傳送一些簡(jiǎn)單的數(shù)據(jù);
  • Form表單一般用于更新資源信息,默認(rèn)使用GET請(qǐng)求類(lèi)型,多使用POST請(qǐng)求類(lèi)型。由于POST請(qǐng)求類(lèi)型理論上沒(méi)有數(shù)據(jù)大小限制,可用表單來(lái)傳較大量的數(shù)據(jù);
  • 重定向
  • 重定向是指由原請(qǐng)求地址重新定位到某個(gè)新地址,原有的request請(qǐng)求失效,客戶端看到的是新的request請(qǐng)求返回的響應(yīng)結(jié)果,客戶端瀏覽器地址欄變?yōu)樾抡?qǐng)求地址。


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


  • 請(qǐng)求轉(zhuǎn)發(fā)的過(guò)程發(fā)生在服務(wù)器內(nèi)部,只能從當(dāng)前應(yīng)用內(nèi)部查找相應(yīng)的轉(zhuǎn)發(fā)資源,而不能轉(zhuǎn)發(fā)到其它應(yīng)用的資源。
  • 請(qǐng)求轉(zhuǎn)發(fā)使用RequestDispatcher接口中的forward()方法來(lái)實(shí)現(xiàn),該方法可以把請(qǐng)求轉(zhuǎn)發(fā)給另外一個(gè)資源,并讓該資源對(duì)此請(qǐng)求進(jìn)行響應(yīng)。
    RequestDispatcher接口有以下兩個(gè)方法:

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

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


請(qǐng)求轉(zhuǎn)發(fā)

重定向
  • 請(qǐng)求轉(zhuǎn)發(fā)與重定向的區(qū)別
    重定向和請(qǐng)求轉(zhuǎn)發(fā)都可以讓瀏覽器獲得另外一個(gè)URL所指向的資源,但兩者的內(nèi)部運(yùn)行機(jī)制有很大的區(qū)別:
  • 請(qǐng)求轉(zhuǎn)發(fā)只能將請(qǐng)求轉(zhuǎn)發(fā)給同一個(gè)Web應(yīng)用中的組件;
    而重定向不僅可以重定向到當(dāng)前應(yīng)用程序中的其他資源,還可以重定向到同一個(gè)站點(diǎn)上的其他應(yīng)用程序中的資源,或者重定向到其他站點(diǎn)的資源;
  • 重定向的訪問(wèn)過(guò)程結(jié)束后,瀏覽器地址欄中顯示的URL會(huì)發(fā)生改變,由初始的URL地址變成重定向的目標(biāo)URL;
    而請(qǐng)求轉(zhuǎn)發(fā)過(guò)程結(jié)束后,瀏覽器地址欄保持初始的URL地址不變;
  • 請(qǐng)求轉(zhuǎn)發(fā)調(diào)用者與被調(diào)用者之間共享相同的request對(duì)象和response對(duì)象,它們屬于同一個(gè)訪問(wèn)請(qǐng)求和響應(yīng)過(guò)程;
    而重定向調(diào)用者與被調(diào)用者使用各自的request對(duì)象和response對(duì)象,它們屬于兩個(gè)獨(dú)立的訪問(wèn)請(qǐng)求和響應(yīng)過(guò)程。
  • 重定向和請(qǐng)求轉(zhuǎn)發(fā)不代表方法的結(jié)束,下面的代碼還會(huì)繼續(xù)執(zhí)行,注意在轉(zhuǎn)發(fā)或重定向的語(yǔ)句后面不要在編寫(xiě)代碼。
三、ServletConfig、ServletContext接口
  • ServletConfig接口
  • 容器在初始化一個(gè)Servlet時(shí),將為該Servlet創(chuàng)建一個(gè)唯一的ServletConfig對(duì)象,并將這個(gè)ServletConfig對(duì)象通過(guò)init(ServletConfig config)方法傳遞并保存在此Servlet對(duì)象中。
  • 使用ServletConfig接口獲取Servlet初始化參數(shù)
    通過(guò)使用@WebServlet注解的initParams熟悉來(lái)配置初始化參數(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也稱(chēng)為Servlet上下文,代表當(dāng)前Servlet運(yùn)行環(huán)境,是Servlet與Servlet容器之間直接通信的接口;
    Servlet容器在啟動(dòng)一個(gè)Web應(yīng)用時(shí),會(huì)為該應(yīng)用創(chuàng)建一個(gè)唯一的ServletContext對(duì)象供該應(yīng)用中的所有Servlet對(duì)象共享,Servlet對(duì)象可以通過(guò)ServletContext對(duì)象來(lái)訪問(wèn)容器中的各種資源。
  • 獲得ServletContext對(duì)象的兩種方式:
    通過(guò)ServletConfig接口的getServletContext()方法獲得ServletContext對(duì)象;
    通過(guò)GenericServlet抽象類(lèi)的getServletContext()方法獲得ServletContext對(duì)象,實(shí)質(zhì)上該方法也是調(diào)用了ServletConfig的getServletContext()方法。
  • ServletContext接口中提供了以下幾種類(lèi)型的方法:
    獲取應(yīng)用范圍的初始化參數(shù)的方法;
    存取應(yīng)用范圍域?qū)傩缘姆椒ǎ?br> 獲取當(dāng)前Web應(yīng)用信息的方法;
    獲取當(dā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對(duì)象可以理解為容器內(nèi)的一個(gè)共享空間,可以存放具有應(yīng)用級(jí)別作用域的數(shù)據(jù),Web應(yīng)用中的各個(gè)組件都可以共享這些數(shù)據(jù)。這些共享數(shù)據(jù)以key/value的形式存放在ServletContext對(duì)象中,并以key作為其屬性名被訪問(wèn)。
  • 應(yīng)用域?qū)傩缘拇嫒》椒?br> setAttribute(String name,Object object),把一個(gè)對(duì)象和一個(gè)屬性名綁定并存放到ServletContext中,參數(shù)name指定屬性名,參數(shù)Object表示共享數(shù)據(jù)
    getAttribute(String name),根據(jù)參數(shù)給定的屬性名,返回一個(gè)Object類(lèi)型的對(duì)象
    getAttributeNames(),返回一個(gè)Enumeration對(duì)象,該對(duì)象包含了所有存放在ServletContext中的屬性名。
    removeAttribute(String name),根據(jù)參數(shù)指定的屬性名,從ServletContext對(duì)象中刪除匹配的屬性
  • 頁(yè)面訪問(wèn)量統(tǒng)計(jì)
@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類(lèi)型和字符編碼方式
      response.setContentType("text/html;charset=UTF-8");
      //獲取ServletContext對(duì)象
      ServletContext ctx = getServletContext();
      //從ctx對(duì)象獲取count屬性存放的計(jì)數(shù)值
      Integer count = (Integer) ctx.getAttribute("count");
      if (count == null) {//判斷是否為第一次訪問(wèn)頁(yè)面
          count = 1;
      } else {
          count = count + 1;
      }
      //將更新后的數(shù)值存放到ServletContext對(duì)象的count屬性中
      ctx.setAttribute("count", count);
      //獲取輸出流
      PrintWriter out = response.getWriter();
      //輸出計(jì)數(shù)信息
      out.println("<p>本網(wǎng)站目前訪問(wèn)人數(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對(duì)象還包含有關(guān)Web應(yīng)用的信息,
例如:當(dāng)前Web應(yīng)用的根路徑、應(yīng)用的名稱(chēng)、應(yīng)用組件間的轉(zhuǎn)發(fā)、以及容器下其他Web應(yīng)用的ServletContext對(duì)象等。

@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類(lèi)型和字符編碼方式
      response.setContentType("text/html;charset=UTF-8");
      // 獲取當(dāng)前ServletContext對(duì)象
      ServletContext context = super.getServletContext();
      // 獲取容器中URL路徑為“/HelloServlet”的應(yīng)用的ServletContext對(duì)象
      ServletContext contextByUrl = context.getContext("/HelloServlet");
      // 獲取當(dāng)前Web應(yīng)用的上下文根路徑
      String contextPath = context.getContextPath();
      // 獲取當(dāng)前Web應(yīng)用的名稱(chēng)
      String contextName = context.getServletContextName();
      // 獲取容器中URL路徑為“/chapter01”的應(yīng)用的應(yīng)用名稱(chēng)
      String contextByUrlName = contextByUrl.getServletContextName();
      // 獲取轉(zhuǎn)發(fā)請(qǐng)求的RequestDispatcher對(duì)象
      RequestDispatcher rd = >context.getRequestDispatcher("/HelloServlet");
      // 獲取輸出流
      PrintWriter out = response.getWriter();
      out.println("<P>當(dāng)前Web應(yīng)用的上下文根路徑是:" + contextPath + >"</p>");
      out.println("<p>當(dāng)前Web應(yīng)用的名稱(chēng)是:" + contextName + "</p>");
      out.println("<p>容器中URL路徑為“/HelloServlet”的應(yīng)用的應(yīng)用名稱(chēng)是:" + contextByUrlName);
  }

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

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


    HTTP協(xié)議的請(qǐng)求報(bào)文
  • 請(qǐng)求報(bào)文信息格式樣例—我們可以通過(guò)firefox瀏覽器的開(kāi)發(fā)者工具查看


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

POST請(qǐng)求亂碼問(wèn)題:

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

GET請(qǐng)求的亂碼問(wèn)題:

  • GET請(qǐng)求參數(shù)以“?”或“&”為連接字符附加在URL地址后,根據(jù)網(wǎng)絡(luò)標(biāo)準(zhǔn)RFC1738規(guī)定,只有字母和數(shù)字以及一些特殊符號(hào)和某些保留字才可以不經(jīng)過(guò)編碼直接用于URL,因此在請(qǐng)求參數(shù)為中文時(shí)必須先由瀏覽器進(jìn)行編碼后才能發(fā)送給服務(wù)器,服務(wù)器端對(duì)GET請(qǐng)求參數(shù)依照服務(wù)器本身默認(rèn)的字符集進(jìn)行解碼。
  • 在服務(wù)器端,由于GET請(qǐng)求參數(shù)是作為請(qǐng)求行發(fā)送給服務(wù)器的,因此Servlet在通過(guò)getParameter()獲取請(qǐng)求參數(shù)時(shí),并不能使用setCharacterEncoding(charset)方法指定的字符集進(jìn)行解碼,而是依照服務(wù)器本身默認(rèn)的字符集進(jìn)行解碼。
  • Tomcat服務(wù)器各版本中默認(rèn)的URIEncoding字符集并不完全相同,例如,Tomcat6和Tomcat7都默認(rèn)為“ISO-8859-1”,這類(lèi)版本中,對(duì)于GET請(qǐng)求的中文參數(shù)必須經(jīng)處理后才會(huì)避免亂碼問(wèn)題,因此在實(shí)際開(kāi)發(fā)中盡量避免使用GET請(qǐng)求來(lái)傳遞中文參數(shù)。
  • HttpServletRequest屬性
  • 存儲(chǔ)在HttpServletRequest對(duì)象中的對(duì)象稱(chēng)之為請(qǐng)求域?qū)傩裕瑢儆谕粋€(gè)請(qǐng)求的多個(gè)處理組件之間可以通過(guò)請(qǐng)求域?qū)傩詠?lái)傳遞對(duì)象數(shù)據(jù)。
  • HttpServletRequest接口提供了如下與請(qǐng)求域?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對(duì)象由Servlet容器在用戶每次請(qǐng)求Servlet時(shí)創(chuàng)建并傳入Servlet的service()方法中。
  • HttpServletResponse接口繼承自ServletResponse接口,是專(zhuān)用于HTTP協(xié)議的子接口,用于封裝HTTP響應(yīng)消息。
  • 在HttpServlet類(lèi)的service()方法中,傳入的ServletResponse對(duì)象被強(qiáng)制轉(zhuǎn)換為HttpServletResponse對(duì)象來(lái)進(jìn)行HTTP響應(yīng)信息的處理。
  • HttpServletResponse接口提供了具有如下功能類(lèi)型的方法:
    設(shè)置響應(yīng)狀態(tài)的方法;
    構(gòu)建響應(yīng)頭的方法;
    創(chuàng)建響應(yīng)正文的方法。
    將服務(wù)器的端口號(hào)調(diào)整為8888,以后的訪問(wèn)路徑為:http://localhost:8888/
  • HTTP協(xié)議響應(yīng)報(bào)文

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



  • HTTP協(xié)議響應(yīng)行
  • HTTP協(xié)議響應(yīng)報(bào)文的響應(yīng)行由報(bào)文協(xié)議和版本以及狀態(tài)碼和狀態(tài)描述構(gòu)成,狀態(tài)碼由三個(gè)十進(jìn)制數(shù)字組成,第一個(gè)十進(jìn)制數(shù)字定義了狀態(tài)碼的類(lèi)型,后兩個(gè)數(shù)字沒(méi)有分類(lèi)的作用。HTTP狀態(tài)碼可分為如下5種類(lèi)型:
  • 常見(jiàn)的響應(yīng)狀態(tài)碼:
    200表示請(qǐng)求成功;
    302表示資源(網(wǎng)頁(yè)等)暫時(shí)轉(zhuǎn)移到其它URL;
    404表示請(qǐng)求的資源(網(wǎng)頁(yè)等)不存在;
    500表示服務(wù)器內(nèi)部錯(cuò)誤。
  • HTTP協(xié)議響應(yīng)消息頭
  • 設(shè)置響應(yīng)消息頭
    對(duì)于一些常用的消息頭, HttpServletResponse提供了一些特定的方法來(lái)進(jìn)行設(shè)置:
    setContentType(String mime),設(shè)定Content-Type消息頭;
    setContentLength(int length),設(shè)定Content-Length消息頭;
    addHeader(String name,String value),新增String類(lèi)型的值到名為name的http頭部;
    addIntHeader(String name,int value),新增int類(lèi)型的值到名為name的http頭部;
    addDateHeader(String name,long date),新增long類(lèi)型的值到名為name的http頭部;
    addCookie(Cookie c),為Set-Cookie消息頭增加一個(gè)值;
  • 實(shí)現(xiàn)一個(gè)頁(yè)面動(dòng)態(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類(lèi)型和字符編碼方式
      response.setContentType("text/html;charset=UTF-8");
      // 設(shè)置響應(yīng)頭“refresh”的值為1秒
      response.setHeader("refresh", "1");
      //獲取輸出流
      PrintWriter out = response.getWriter();
      out.println("現(xiàn)在時(shí)間是:");
      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ù)是通過(guò)輸出流對(duì)象來(lái)完成的,HttpServletResponse接口提供了兩個(gè)獲取不同類(lèi)型輸出流對(duì)象的方法:
    1.getOutputStream(),返回字節(jié)輸出流對(duì)象ServletOutputStream;
    ServletOutputStream對(duì)象主要用于輸出二進(jìn)制字節(jié)數(shù)據(jù)。例如,配合setContentType()方法響應(yīng)輸出一個(gè)圖像、視頻等。
    2.getWriter(),返回字符輸出流對(duì)象PrintWriter;
    PrintWriter對(duì)象主要用于輸出字符文本內(nèi)容,但其內(nèi)部實(shí)現(xiàn)仍是將字符串轉(zhuǎn)換成某種字符集編碼的字節(jié)數(shù)組后再進(jìn)行輸出。
  • 當(dāng)向ServletOutputStream或PrintWriter對(duì)象中寫(xiě)入數(shù)據(jù)后,Servlet容器會(huì)將這些數(shù)據(jù)作為響應(yīng)消息的正文,然后再與響應(yīng)狀態(tài)行和各響應(yīng)頭組合成完整的響應(yīng)報(bào)文輸出到客戶端,同時(shí),在Serlvet的service()方法結(jié)束后,容器還將檢查getWriter()或getOutputStream()方法返回的輸出流對(duì)象是否已經(jīng)調(diào)用過(guò)close()方法,如果沒(méi)有,容器將調(diào)用close()方法關(guān)閉該輸出流對(duì)象。
  • 實(shí)現(xiàn)響應(yīng)輸出一個(gè)圖像
@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對(duì)象
              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);//向輸出流中寫(xiě)入二進(jìn)制數(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)輸出中文亂碼問(wèn)題
  • 由于Java程序中的字符文本在內(nèi)存中以Unicode編碼的形式存在,因此PrintWriter對(duì)象在輸出字符文本時(shí),需要先將它們轉(zhuǎn)換成其他某種字符集編碼的字節(jié)數(shù)組后輸出。
  • PrintWriter對(duì)象默認(rèn)使用ISO-8859-1字符集進(jìn)行Unicode字符串到字節(jié)數(shù)組的轉(zhuǎn)換,由于ISO-8859-1字符集中沒(méi)有中文字符,因此Unicode編碼的中文字符將被轉(zhuǎn)換成無(wú)效的字符編碼后輸出給客戶端,這就是Servlet中文輸出亂碼問(wèn)題的原因。
  • ServletResponse接口中定義了三種方等方法來(lái)指定getWriter()方法返回的PrintWriter對(duì)象所使用的字符集編碼。
    1.response.setCharacterEncoding("UTF-8");
    只能用來(lái)設(shè)置PrintWriter輸出流中字符的編碼方式,它的優(yōu)先權(quán)最高。
    2.(推薦使用)response.setContentType("text/html;charset=UTF-8");
    既可以設(shè)置PrintWriter輸出流中字符的編碼方式,也可以設(shè)置瀏覽器接收到這些字符后以什么編碼方式來(lái)解碼,它的優(yōu)先權(quán)低于第一種方法。
    3.response.setLocale(new java.util.Locale("zh","CN"));
    只能用來(lái)設(shè)置PrintWriter輸出流中字符的編碼方式,它的優(yōu)先權(quán)最低,在已經(jīng)使用前兩種方法中的一個(gè)設(shè)置了編碼方式以后,它將被覆蓋而不再起作用。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容