Servlet

     一、Servlet簡介

  • Servlet是服務器端的重要組件,直譯爲服務端的小程序,它屬於動態資源,用來處理請求,服務器接收到請求後會調用Servlet來處理請求。
  • Servlet 的主要做用
      • 接收請求
      • 處理請求
      • 完成響應
  • 例如:
當咱們要完成一個登陸功能時,用戶會將輸入的用戶名和密碼以 POST 請求的形式發送到服務器,可是服務器自己並不具備能力來讀取用戶發送的用戶名和密碼,也就不可能對用戶名和密碼進行驗證,因此當服務器收到這類請求後須要將請求轉個一個Servlet處理。
  • Servlet 實現類由咱們編寫,而由 Web 服務器(Servlet容器)調用,每一個 Servlet 都必須實現javax.servlet.Servlet
 
 
  1. HelloServlet

  • 步驟:
      1. 建立動態WEB項目WEB_Servlet
      2. 項目下建立包com.atguigu.web.servlet
      3. 包下建立一個類HelloServlet並實現javax.servlet.Servlet接口
      4. HelloServlet 的service()方法中加入一行打印語句System.out.prin tln(「hello」);
      5. WEB-INF 目錄下的web .xml 文件中註冊映射Servlet
        <servlet>
                 <servlet-name> HelloServlet </servlet-name>
                                       <servlet-class> com.atguigu.web.servlet .HelloServlet</servlet-class>
              </servlet>   
                               <servlet-mapping>
        <servlet-name> HelloServlet </servlet-name>
        <url-pattern>/HelloServlet</url- pattern >
       </servlet-mapping>
  1. 啓動服務器,在瀏覽器中訪問:http://localhost:8080/WEB_Servlet / HelloServlet
  • 具體代碼
類:com.atdongruan.web.servlet .HelloServlet
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}
@Override
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {System.out.println("hello");}
@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}

 

web. xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <servlet>
       <servlet-name>HelloServlet</servlet-name>
       <servlet-class>com.atdongruan.web.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
       <servlet-name>HelloServlet</servlet-name>
       <url-pattern>/HelloServlet</url-pattern>
    </servlet-mapping>
</web-app>/*
  •  web.xml文件詳解:
    • 由於建立好的Servlet須要由Servlet容器調用,而Servlet容器並不能知道咱們所建立的Servlet的存在,因此須要在web.xml文件中進行註冊。
      • <servlet></servlet>用於註冊servlet
      • <servlet-name>用於設置servlet的名字,在服務器中關於servlet的一切配置都須要servlet-name來進行配置
      • <servlet-class>用於設置servlet的全類名,用於建立servlet的實例(反射)
    • 而僅僅註冊是遠遠不夠的,由於Servlet是用來處理客戶端發送的請求的,因此還須要爲Servlet映射一個請求地址。
      • <servlet-mapping>用於映射須要servlet處理的請求地址
      • <servlet-name>爲servlet的名字,和<servlet>中的<name>有對應關係
      • <url-pattern>須要servlet處理的請求地址
      • */

 

3.1 Servlet生命週期html

  • 生命週期,以人舉例的話,從人的出生到死亡稱爲一我的的生命週期,在人的整個生命中有不少的階段,但總的來講能夠大體分爲三個階段:出生、工做、死亡。每一階段都有每一階段要作的事。對於咱們的Servlet也是同樣。
  • Servlet的生命週期指的是Servlet由實例化到被銷燬的過程。一樣也被分爲了三個階段:實例化、處理請求、被銷燬。而每一個階段咱們都有對應的方法來實現響應的功能,在實例化階段須要調用init()方法來作初始化操做,處理請求階段調用service()方法處理請求,銷燬對象以前調用 destroy ()作釋放資源等操做。
  • Servlet生命週期相關方法
    • public void init(ServletConfig config)
    • public void service(ServletRequest req, ServletResponse res)
    • public void destroy()

 

3.1.1 Servlet初始化java

  • 服務器會在Servlet第一次處理請求、或服務器啓動時建立Servlet實例,默認是在第一次處理請求時進行實例化的。
  • 對於每一個Servlet,服務器只會建立一個Servlet實例。以咱們的 HelloServlet 爲例,當咱們經過瀏覽器訪問http://localhost:8080/WEB_Servlet / HelloServlet時,服務器會根據路徑/HelloServlet從配置文件中找到url-pattern值爲/HelloServlet的servlet -mapping ,而後在 servlet-mapping 中找到servlet-name爲 HelloServlet 。接下來,找到servlet-name爲HelloServlet的< servlet >。最後,得到 servlet 的全類名,經過全類名建立類的實例,這個實例會放到一個集合中。這一過程後容器不再會對該servlet作實例化操做,而是直接從集合中獲取servlet實例處理請求。
  • Servlet實例化後會當即調用public void init(ServletConfig config)方法。這裏主要作一些獲取配置信息等在處理請求前須要作的準備工做。
  • init() 方法在Servlet的整個生命週期中只會被調用一次。
  • init(ServletConfig config) 方法被調用時,容器會傳遞一個 ServletConfig 對象做爲參數,該對象能夠獲取Servlet相關的配置信息。

3.1.2 Servlet處理請求

  • Servlet 收到請求時,服務器會調用Servlet的service()方法來處理請求。每收到一次請求就調用一次 service() 方法,因此service()方法是被屢次調用的。所以,咱們開發時主要的業務都發生在service()方法中。
  • service(ServletRequest req, ServletResponse res) 被調用時,榮器會建立一個ServletRequest和一個ServletResponse對象做爲參數傳遞進來,ServletRequest對象主要封裝了客戶端發送過來的請求信息,ServletResponse對象主要用來封裝了服務器響應給客戶端的數據。關於這兩個對象後邊咱們還要詳細的學習,這裏就先不討論了。
    1. Servlet銷燬

  • 通常狀況下Servlet對象不會被銷燬,但當服務器關閉或者重啓時Servlet對象也將被銷燬,這時在servlet被銷燬前它的destroy()方法會被調用,該方法主要作一些銷燬前的收尾工做,好比:釋放資源、保存數據等。在實際應用中,這類工做主要由其餘對象完成因此該方法並不經常使用。
  • 思考:
    • Servlet容器是如何銷燬servlet對象的?
    • 如何測試Servlet的生命週期?
  1. Servlet相關接口

  • ServletRequestServletResponse

    • ServletRequest是由容器建立並傳遞到service () 方法中,容器所建立的對象其實是HttpServletRequest,因此開發中咱們都會將 ServletRequest 強轉成HttpServletRequest。
    • HttpServletRequest的方法:
      • String getParameter(String paramName):獲取指定請求參數的值;
      • String getMethod():獲取請求方法,例如GET或POST;
      • String getHeader(String name):獲取指定請求頭的值;
      • void setCharacterEncoding(String encoding):設置請求體的編碼!由於GET請求沒有請求體,因此這個方法只只對POST請求有效。當調用request.setCharacterEncoding( utf-8 )以後,再經過getParameter()方法獲取參數值時,那麼參數值都已經經過了轉碼,即轉換成了UTF-8編碼。因此,這個方法必須在調用getParameter()方法以前調用!
    • HttpServletResponse的方法
      • PrintWriter getWriter():獲取字符響應流,使用該流能夠向客戶端輸出響應信息。例如response.getWriter().print( <h1>Hello JavaWeb!</h1> );
      • ServletOutputStream getOutputStream():獲取字節響應流,當須要向客戶端響應字節數據時,須要使用這個流,例如要向客戶端響應圖片;
      • void setCharacterEncoding(String encoding):用來設置字符響應流的編碼,例如在調用setCharacterEncoding( utf-8 );以後,再response.getWriter()獲取字符響應流對象,這時的響應流的編碼爲utf-8,使用response.getWriter()輸出的中文都會轉換成utf-8編碼後發送給客戶端;
      • void setHeader(String name, String value):向客戶端添加響應頭信息,例如setHeader( Refresh ,  3;url=http://www. atguigu.com」 ),表示3秒後自動刷新到http:// www. atguigu.com
      • void setContentType(String contentType):該方法是setHeader( content-type ,  xxx )的簡便方法,即用來添加名爲content-type響應頭的方法。content-type響應頭用來設置響應數據的MIME類型,例如要向客戶端響應jpg的圖片,那麼能夠setContentType( image/jepg ),若是響應數據爲文本類型,那麼還要臺同時設置編碼,例如setContentType( text/html;chartset=utf-8 )表示響應數據類型爲文本類型中的html類型,而且該方法會調用setCharacterEncoding( utf-8 )方法;
      • void sendError(int code, String errorMsg):向客戶端發送狀態碼,以及錯誤消息。例如給客戶端發送404:response( 404, 「 您要查找的資源不存在! )。
  • ServletConfig

    • ServletConfig對象對應着web.xml文件中的一個< servlet >元素,例如:想要獲取元素中的<servlet-name>的值,那麼可使用servletConfig.getServletName () 方法來獲取。
    • Servlet Config 對象一樣有容器建立,而後做爲參數傳遞給init()方法,能夠在init()方法中使用。
    • Servlet Config 的方法:
      • String getServletName():獲取Servlet在web.xml文件中的配置名稱,即<servlet-name>指定的名稱;
      • ServletContext getServletContext():用來獲取ServletContext對象,ServletContext會在後面講解;
      • String getInitParameter(String name):用來獲取在web.xml中配置的初始化參數,經過參數名來獲取參數值;
      • Enumeration getInitParameterNames():用來獲取在web.xml中配置的全部初始化參數名稱;
      • web.xml 中爲servlet配置初始化參數
        <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>省略</servlet-class>
        <init-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
        </init-param>
        </servlet>
 
  • 每個 init-param 表示一個初始化參數,經過getInitParameter( String name )能夠根據param-name的值獲取到param-value的值。
  • 每一個 servlet 中能夠配置多個init-param, servlet 只能獲取自身的初始化參數,而不能得到其餘servlet的初始化參數。
  1. GenericServlet

  • 在顯示開發中咱們若是直接實現servlet接口功能也是能夠正常實現的,可是所面臨的問題是:若是我直接實現Servlet接口,那接口中的全部方法都必需要實現,可是這些方法有的咱們用不到,而有的方法實現起來很麻煩並且沒給servlet實現的代碼都差很少,因而咱們就須要一個抽象類來幫助咱們實現servlet接口,實現一些通用的方法,而咱們只須要繼承 GenericServlet 就能夠了,這樣的好處是咱們不在須要重寫所有方法,而只須要重寫必須的和咱們須要的方法就能夠。
  • 代碼:
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable{
    private transient ServletConfig config;
    public GenericServlet() { }
    public void destroy() {}
    public String getInitParameter(String name) {
                   return getServletConfig().getInitParameter(name);
    }
    public Enumeration getInitParameterNames() {
                   return getServletConfig().getInitParameterNames();
    }  
    public ServletConfig getServletConfig()  {return config;  }
    public ServletContext getServletContext() {
                   return getServletConfig().getServletContext();
    }
    public String getServletInfo() { return ""; }
    public void init(ServletConfig config) throws ServletException {
                   this.config = config;
                   this.init();
    }
    public void init() throws ServletException {}
    public void log(String msg) {
                   getServletContext().log(getServletName() + ": "+ msg);
    }
    public void log(String message, Throwable t) {
                   getServletContext().log(getServletName() + ": " + message, t);
    }
    public abstract void service(ServletRequest req, ServletResponse res)
         throws ServletException, IOException;
    public String getServletName() {  return config.getServletName(); }
}

 

  • GenericServlet中有兩個重載的init()方法,因爲 ServletConfig 對象是在容器調用init(ServletConfig config)方法是傳過來的,只能在該方法中使用因此咱們在類中定義了一個類型爲ServletConfig的對象,而在init(ServletConfig config)方法中對該變量進行賦值。這樣作就會有一個問題存在,若是子類在繼承父類時重寫了該方法,那賦值操做將不會被調用,這時若是使用ServletConfig的方法將致使空指針異常。因此,在這個類中又定義了一個init()方法,這個方法將在init(ServletConfig config)方法中被調用,而且是在config對象賦值以後調用,而初始化的操做咱們能夠經過重寫config()方法來完成,這樣作既保證了對ServletConfig的賦值,又能夠正常作初始化操做。
  • 像這種將固定的代碼編寫到一個方法中,而將有可能發生變化的代碼交給子類編寫。其實是一種叫作模板模式的設計模式,而像 init() 這種沒有方法體,而又被父類方法調用,須要子類重寫的方法稱爲鉤子方法。
  • GenericServlet 同時還實現了ServletConfig接口,實現這個接口的好處是咱們直接經過this就能夠調用ServletConfig對象的方法了,而這些方法實際調用的都是,成員變量config的方法。
  1. HttpServlet

  • Http Servlet 是GenericServlet的子類,他是 Tomcat 中專門處理HttpServlet的 Servlet (實際上Tomcat只處理Http類型的請求)因此在通常狀況下咱們都會經過繼承 HttpServlet 來實現servlet。
  • HttpServlet和GenericServlet同樣都是一個抽象類,也就是不能對它們進行實例化,與GenericServlet不一樣HttpServlet中沒有任何抽象方法,因此須要咱們根據實際需求來重寫它的方法。
  • 在GenericServlet中service(ServletRequest req, ServletResponse res)方法是一個抽象方法,必需要由子類實現,而在HttpServlet中已經被實現,而在HttpServlet的實現的方法中會被強制轉換成Http ServletRequest 和Http ServletResponse (不用擔憂會出現類型轉換異常,由於傳過來的對象自己就是該類型)。轉換類型以後會調用HttpServlet中的一個重載的service(HttpServletRequest req, HttpServletResponse resp)方法。也就是說若是咱們要重寫service()方法沒必要再去重寫GenericServlet的service()方法了,而能夠直接重寫HttpServlet重載的service()方法就能夠了。
  • service(HttpServletRequest req, HttpServletResponse resp) 方法中,會對它的請求類型進行判斷,而後根據請求類型的不一樣再去調用不一樣的方法。如: post 類型的請求回去調用doPost(), get 請求回去調用doGet(),delete請求回去調用doDelete(),以此類推。可是在實際應用中咱們只會使用到doPost()和 doGet() ,因此通常狀況下咱們不會去重寫service()方法,而是去重寫更簡單的doGet()或者 doPost()
  1. Servlet擴展

 

問題1:Servlet的構造器調用了幾回?web

  • 這個問題實際上很容易測試,只須要在Servlet的中寫一個無參構造器,在方法中寫一個打印語句,而後向該Servlet發送請求,會發現打印語句僅僅輸出了一次,由此證實構造器只調用了一次。上邊咱們也說過,Servlet是單實例的,而調用構造器就是用來建立實例的。因此構造器只會被調用一次。

 

問題2:Servlet是線程安全的嗎?apache

  • 因爲 Servlet 是單實例的,因此當容器調用service方法處理請求時是以多線程的方式調用的,可是由於性能問題因此在這個方法中並無考慮同步的問題,因此Servlet並非線程安全的,可是這樣作的好處是性能較好。
  • 因爲 Servlet 不是線程安全的,因此儘可能不要在使用 Servlet 處理請求時操做變量,由於有可能會出現同步的問題。(實際應用中只有很是高的併發的狀況下才有可能出現這個問題,雖然如此但仍是儘可能不要那麼作)。

 

問題3:Servlet實例只能在第一次請求時被建立嗎?小程序

  • 通常狀況下Servlet會在第一次收到請求時被建立,因此當咱們第一次訪問某個Servlet時會比平時慢一些,這種咱們稱爲第一次懲罰。
  • 若是但願在服務器啓動時就建立Servlet的實例,能夠在web.xml中進行配置,在servlet標籤中還有一個load-on-startup標籤,這個標籤須要一個大於等於0的整數做爲參數,當配置了這個屬性後,該Servlet將會在服務器啓動時就被建立,且值越小建立的時機越早。

問題4:url-pattern映射的規則是什麼?

  • url-pattern 配置的Servlet映射的地址,他的配置規則以下:
    • 精確匹配:
      • 當前項目下指定的URL必須徹底
      • 如: /path/ServletName
      • 只有URL爲:http://localhost:8080/項目名 /path/ServletName
      • 由此也可知/是表明的項目根目錄,而在html中/表明的是服務器根目錄
    • 路徑匹配:
      • 當前項目下指定路徑的URL地址
      • 如:/path/*
      • URL 爲:http://localhost:8080/項目名 /path/ 任意值
    • 全匹配:
      • 當前項目下全部的 URL 地址
      • 如:/*
      • 全部URL均可以
    • 後綴匹配:
      • 當前項目下指定後綴的 URL 地址
      • 如:*.action
      • URL 爲:http://localhost:8080/項目名 / 任意值 .  action
  • 優先級:
    • 一、精確匹配
    • 二、路徑匹配
    • 三、全匹配
    • 四、後綴匹配
    • 五、還有一種 / ,這種也是所有匹配,優先級最低
  • 關於 *
    • * 就是通配符,匹配任意字符
    • * 只能出現前面和後面,不能出如今中間
      • 如:/*.action錯誤
    • * 只能出現一次
      • 如: */* 錯誤
    • * 不能單獨出現
      • 如: *  錯誤

 

問題5:web.xml文件僅僅是看到的那麼簡單嗎?設計模式

  • web.xml 文件整個項目中的配置文件,很是重要。可是縱觀咱們項目下的web.xml文件彷佛內容很少,那如此重要的文件爲何只配置的這麼少的內容呢?實際上在 Tomcat 中還有一個總的web.xml文件,就在% CATALINA_HOME % /conf 目錄下。
  • 重要配置:
  • DefaultServlet :用於處理靜態資源的默認Servlet
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

  

  • JspServlet :用於處理JSP的Servlet
    <servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
    <param-name>fork</param-name>
    <param-value>false</param-value>
    </init-param>
    <init-param>
    <param-name>xpoweredBy</param-name>
       <param-value>false</param-value>
     </init-param>
     <load-on-startup>3</load-on-startup>
    </servlet>
    <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jsp</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
    

      

  • s ession的過時時間
<session-config>
        <session-timeout>30</session-timeout>
    </session-config>

 

  • MIME類型
 
  1. ServletContext

 

<mime-mapping>
<extension>123</extension>
  <mime-type>application/vnd.lotus-1-2-3</mime-type>
 </mime-mapping>

  

8.1 ServletContext簡介瀏覽器

  • 每一個 WEB 應用服務器都會爲其建立一個ServletContext對象,項目啓動時ServletContext對象被建立,項目中止或從新加載時ServletContext對象被銷燬
  • Servlet Context 對象主要做用就是在 Servlet 之間共享數據和加載WEB應用的配置信息,還記得ServletConfig對象能夠獲取到每一個Servlet的配置信息吧,而咱們的ServletContext能夠或取整個WEB應用的配置信息。

 

8.2 獲取ServletContexttomcat

  • Serlet 接口中,能夠經過init方法中的ServletConfig調用 getServletContext() 方法來得到ServletContext對象。
  • GenericServlet和HttpServlet能夠直接調用getServletContext()方法。(實際上也是 ServletConfig 的getServletContext()方法)。
  • HttpSession 對象的 getServletContext ()方法一樣也能夠獲取。

 

8.3 域對象安全

  • ServletContext 是JavaWeb的四個域對象之一
    • PageContext
    • ServletRequest
    • HttpSession
    • ServletContext
  • 域對象主要用來存儲傳遞數據,每一個域對象的內部都有一個map用來存儲對象
  • 讀取數據的方法:
    • void setAttribute(String name, Object value):用來存儲一個對象,也能夠稱之爲存儲一個域屬性,例如:servletContext.setAttribute( 「key」 ,  「value」 ),在ServletContext中保存了一個域屬性,域屬性名稱爲 key ,域屬性的值爲 value
    • Object getAttribute(String name):用來獲取ServletContext中的數據;
    • void removeAttribute(String name):用來移除ServletContext中的域屬性,若是參數name指定的域屬性不存在,那麼本方法什麼都不作;
    • Enumeration getAttributeNames():獲取當前域中全部屬性的名稱;

 

8.4 獲取當前應用的初始化參數服務器

  • ServletConfig 功能相似,ServletContext也能夠獲取初始化參數,這不過config獲取的是當前Servlet的而context得到的是整個Web應用的;
  • 因爲整個應用中都擁有同一個ServletContext因此全部Servlet都能得到相同的初始化參數。
  • web.xml 中配置初始化參數,在根標籤中建立context-param元素,經過元素中param-name和param-value標籤分別配置key和value,每個 context-param 表明一條鍵值對的初始化參數。
 
  <context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
  </context-param>
  <context-param>
    <param-name>password</param-name>
    <param-value>1234</param-value>
  </context-param>
</web-app>

  

 

8.5 獲取項目根目錄

  • 項目根目錄就是端口號後邊的第一個路徑。如:http://localhost:8080 /hello /HelloServlet,紅色部分/hello就是項目的根目錄。這樣作的目的是,由於在項目在開發時或交付後,項目名頗有可能被修改,也就是紅色部分會變成其餘內容,當項目根目錄修改後,頁面中不少資源的路徑會失效,因此須要動態獲取根目錄。
  • 主要經過servletContext.  getContextPath() 來獲取。
  • 經過 HttpServletRequest 對象也能夠得到該值。

 

8.6 獲取資源的流和真實路徑(物理路徑)

  • web 項目中,咱們訪問服務器上的資源通常都是使用URL地址或者相對路徑,可是有時咱們會須要獲取服務器中的文件流,或者獲取文件的物理地址(好比在上傳或下載文件時)。
  • 獲取文件流:
    • InputStream in = getServletContext().getResourceAsStream("/1.jpg");
    • 該方法會獲取項目根目錄下1. jpg 的輸入流
  • 獲取文件真實路徑
    • String realPath = getServletContext().getRealPath("/1.jpg");
    • 該方法得到項目根目錄下1. jpg 的路徑地址
    • 注意:給方法並不會判斷文件是否存在
  1. 請求和響應

    1. 請求和響應的過程

    1. HttpServletResponse對象

  • HttpServletResponse對象封裝了服務器響應給客戶端的信息,該對象由服務器建立,在Servlet處理請求時,服務器會調用Servlet的service方法並將HttpServletResponse對象做爲參數傳遞。因此,咱們能夠直接在service方法中使用該對象。通常咱們習慣簡稱他爲response。
  • response的主要功能有:
    • 設置響應頭信息
      • response.setHeader("Refresh", "3;URL=http://www.baidu.com")
      • response.setContentType("text/html;charset=UTF-8");
    • 設置狀態碼
      • response.sendError(404,"訪問的資源未找到");
    • 設置響應體
      • response.getWriter().print("<h1>Hello World</h1>");
    • 重定向
      • response.sendRedirect("index.html");
    1. HttpServletRequest對象

  • HttpServletRequest對象封裝了客戶端發送給服務器的信息,該對象由服務器建立,在Servlet處理請求時,服務器會調用Servlet的service方法並將HttpServletRequest對象做爲參數傳遞。因此,咱們能夠直接在service方法中使用該對象。通常咱們習慣簡稱他爲request。
  • request的主要功能有:
    • 獲取請求參數
      • String username = request.getParameter("username");
    • 在請求域中讀寫數據
      • request.setAttribute("key1", "value1");
      • String key1 = (String) request.getAttribute("key1");
    • 獲取項目名
      • String contextPath = request.getContextPath();
    • 轉發請求
      • request.getRequestDispatcher("/index.html").forward(request, response);
    1. 轉發和重定向

 

9.4.1 轉發

  • 轉發是經過 request 對象發起的,經過request對象獲取一個RequestDispatcher對象,經過RequestDispatcher的forward方法發起轉發。
  • 轉發是在服務器內部進行的:
    • 整個過程瀏覽器只發送了一個請求。
    • 瀏覽器不能知道轉發行爲的發生。
    • 因爲在服務器內部進行,因此轉發以項目路徑爲根目錄,輸入地址時不須要輸入項目名。
  • 轉發是一次請求,因此request中的數據能夠共享。
  • 轉發只能轉發到應用內部的資源,而不能轉發到其餘應用

 

9.4.2 重定向

  • 重定向是經過response對象發起的,經過response的sendRedirect()方法進行重定向。
  • 重定向是在瀏覽器中進行的:
    • 整個過程當中,瀏覽器發送了兩次請求。
    • 瀏覽器知道轉發行爲的發生。
    • 因爲在瀏覽器端進行,重定向的路徑是以服務器目錄爲根目錄,因此輸入地址時須要輸入項目名。
  • 重定向是兩次請求,不能共享request中的數據。
  • 重定向不僅限定於內部資源,能夠重定向到任意web資源。

 

9.5 路徑問題

  • 一般咱們訪問一個web應用地址格式以下:http://localhost:8080/MyWeb/HelloServlet
  • http://localhost:8080 這一部分咱們稱它爲服務器的根目錄
  • /MyWeb  這一部分咱們稱它爲項目的根目錄
  • /HelloServlet  這一部分是咱們Servlet的映射地址
  • 絕對路徑和相對路徑
    • 絕對路徑:使用 /  開頭的路徑稱爲決定路徑,絕對路徑表示從根目錄開始尋找資源。
    • 相對路徑:不使用   /  開頭的路徑稱爲相對路徑,相對路徑表示從當前資源所在目錄開始尋找資源

 

9.5.1 服務器端路徑

  • 服務器端路徑,主要指在Servlet中使用轉發時的路徑。
  • 服務器端的根目錄指的是項目的根目錄,也就是咱們的項目名。
  • 例如,咱們如今訪問以下地址的Servlet:
    • request.getRequestDispatcher(" index.html ").forward(request, response);
  • 在實際應用中,因爲咱們的資源(Servlet和JSP)所在的位置有可能會發生變更,因此一般咱們會使用絕對路徑。

9.5.2 客戶端路徑

  • 客戶端路徑,主要是值在頁面中引用外部資源,以及在Servlet中作重定向操做時的路徑。
  • 客戶端路徑的根目錄指的是咱們tomcat的服務器的根目錄,也就是項目名前面那段路徑。
  • 例1:咱們如今訪問以下地址的Servlet:
    • response.sendRedirect("index.html");
  • 例2:在 MyWeb 項目中有 form.html 頁面,目錄結構以下:
    • webapps/MyWeb/hello/form.html
    • 如今我在form.html中建立超連接訪問/ hello/HelloServlet
    • 鏈接格式以下:
      • <a  href=」/HelloServlet」 > HelloServlet </ a >
        • 使用絕對路徑,網頁和重定向的根目錄相同,都是服務器的根目錄
        • 所以點擊超連接後會訪問以下地址
          • http://localhost:8080 /HelloServlet
          • 這個地址明顯不對,因此應該從項目名開始寫起
          • 正確以下:
            • <a  href=」/MyWeb/hello/HelloServlet」 > HelloServlet </ a >
            • 點擊後訪問地址:
              • http://localhost:8080 /MyWeb/hello/HelloServlet
    • <a  href=」HelloServlet」 > HelloServlet </a>
      • 使用相對路徑,會從當前html所在目錄開始尋找資源,也就是從/MyWeb/hello/開始。
      • 所以點擊超連接後會訪問以下地址:
        • http://localhost:8080 /MyWeb/hello/HelloServlet
  • 一樣的,在實際開發中客戶端的路徑咱們也會使用絕對路徑,而不使用相對路徑。
  • 可是,這塊有一個問題,在實際開發中咱們項目名有可能會改變,好比:開發中的名字可能爲DMS,而實際部署時就變成了Baidu_DMS。可是這是咱們在項目中的路徑是以/DMS開頭的,那就意味着,咱們要把項目中全部的頁面中、 Servlet 中的/DMS修改爲/Baidu_DMS,如此一來工做量是十分大的,那要如何解決呢?實際上咱們能夠經過request對象動態的獲取項目名來解決這個問題,可是這還須要配合JSP使用,因此咱們在這裏先不考慮該問題。
    1. 編碼問題

 

9.5.1 爲何要使用編碼

9.5.2 常見的編碼

  • ASKII
    • 最先的字符編碼,使用一個字節中的7位來表示,一共只有128個。
  • ISO-8859-1
    • ISO 組織在ASKII基礎上定義的編碼,用來對其進行擴展,使用一個字節中的8位來表示,一共有256個。
  • GB2312
    • 國標碼,中國規範,主要加入的許多中文。
  • GBK
    • 一樣也是國標碼,對 GB2312 的擴充其中添加了更多的中文。
  • GB18030
    • 也是國標碼,國家強制標準,於GB2312兼容,但實際系統中應用並不普遍。
  • UTF-8
    • 萬國碼,支持所有字符,存儲效率較高,咱們所使用的編碼

 

9.5.3 請求編碼

  • 請求編碼是瀏覽器發送給服務器的編碼格式。
  • 瀏覽器發送給服務器的請求編碼主要由頁面中的Content-Type響應頭的編碼決定,例如值爲: Content-Type: text/html; charset=utf-8 ,那麼頁面請求將以utf-8的編碼發送。那麼,就要求咱們必需要將Content-Type的編碼設置成utf-8。
  • 雖然瀏覽器發送來的編碼格式已經肯定,可是還要注意咱們這裏服務器中解析編碼的格式並非 utf-8 還須要咱們設置。
  • Tomcat 中默認以iso解析請求,而瀏覽器是以utf-8發送過來請求,如此一來一定會出現亂碼。因此還須要設置解析請求的編碼:
    • POST 請求
      • POST請求比較簡單,只須要在經過 request 對象獲取請求參數以前調用request.setCharacterEncoding("utf-8")來指定編碼。
    • GET請求
      • 因爲 Tomcat 收到請求後默認會已iso對咱們的url在進行解碼,因此若是直接設置request的編碼是不行的還須要設置咱們Tomcat解析uri的默認編碼。
      • 在% CATALINA_HOME%/conf/server.xml 文件中,找到<Connector />標籤在標籤中加入屬性URIEncoding="utf-8"
< Connector  connectionTimeout = "20000"  port = "8080"  protocol = "HTTP/1.1"  redirectPort = "8443"  URIEncoding = "utf-8"  />

 

9.5.4 響應編碼

  • 響應編碼是服務器發送瀏覽器的編碼格式。
  • Tomcat默認以ISO-8859-1編碼響應請求,可是該編碼並不支持中文,因此咱們在響應中文數據時不能使用該編碼,而應該使用 UTF-8
  • 設置響應編碼:
    • response.setCharacterEncoding("UTF-8");
      • 經過 setCharacterEncoding 設置的是字符輸出流的編碼。
  • 這裏設置的只是輸出的字符編碼,可是做爲瀏覽器來講會默認已GBK去解析,因此還須要經過設置請求頭告訴瀏覽器如何解析。
    • response.setContentType("text/html;charset=UTF-8");
  • 實際上咱們在調用response.setContentType("text/html;charset=UTF-8")時,就已經將咱們的輸出編碼也同時設置了,因此只須要調用此方法便可。

 

9.5.5 URL編碼

  • 客戶端在向服務器發送請求時須要對非英文數據進行編碼操做,編碼後在將請求發送服務器,而服務器在收到請求後會自動進行解碼操做來解析請求。還記得咱們在 server.xml 中配置的URIEncoding= UTF-8 吧,它就是來配置 Tomcat 服務器以何種編碼對URL進行解碼操做的。
  • URL編碼並非字符編碼,而是瀏覽器在字符編碼的基礎上將其轉換成試用於互聯網傳輸的編碼格式。
  • 例如 字的三中編碼分別爲:
    • Byte 編碼:[-26, -99, -114]
    • URL 編碼: %E6%9D%8E
    • 解碼後:李
  • java 中可使用兩個類分別進行編碼和解碼操做
    • 編碼:import java.net.URLEncoder;
      • 例如:String encode = URLEncoder.encode("李 ","utf-8");
    • 解碼 import java.net.URLDecoder;
      • 例如:String decode = URLDecoder.decode("%E6%9D%8E ","utf-8");
相關文章
相關標籤/搜索