1. 程式人生 > >JavaWeb(一)之細說Servlet

JavaWeb(一)之細說Servlet

troy 文件名 ngs 很多 generics 頁面 虛擬 ats att

前言

  其實javaWeb的知識早就學過了,可是因為現在在搞大數據開發,所以web的知識都忘記了。準備開始慢慢的把Web的知識一點一點的回憶起來,多學一點沒有關系,就怕到時候要用的話,什麽都不會了。

一、Servlet概述

1.1、Servlet簡介

  Servlet 運行在服務端的Java小程序,是sun公司提供一套規範(接口),用來處理客戶端請求、響應給瀏覽器的動態資源。但servlet的實質就是java代碼,通過java的API 動態的向客戶端輸出內容。

  Servlet是Java Web的三大組件(Servlet,Filter,Listener)之一,屬於動態資源 ,運行在 Web 服務器或應用服務器上的程序作用為處理請求,服務器會把接收的請求交給Servlet來處理,在Servlet中通常需要:

    接受請求數據、處理請求、完成響應

  例如:客戶端發出登錄請求,或輸出註冊請求,這些請求都應該有Servlet來完成處理。每個Servlet都必須實現javax.servle.Servlet接口。

  總結:   

    處理請求和發送響應的過程是由一種叫做Servlet的程序來完成的,並且Servlet是為了解決實現動態頁面而衍生的東西。理解這個的前提是了解一些http協議的東西,並且知道B/S模式(瀏覽器/服務器)。

    B/S:瀏覽器/服務器。 瀏覽器通過網址來訪問服務器,比如訪問百度,在瀏覽器中輸入www.baidu.com,這個時候瀏覽器就會顯示百度的首頁。

  技術分享

  補充1:

    servlet規範(sun公司自己制定了一種用於擴展web服務器功能的組件規範):包含三個技術點(三大組件)   

    servlet技術
    filter技術---過濾器
    listener技術---監聽器

    1)擴展web服務器功能

      web服務器(tomcat、Weblogic、iis、apache)沒有處理動態資源請求的能力(即該請求需要計算),只能處理靜態資源的請求(如果瀏覽器請求某個html頁面,
      web服務器查看請求的html頁面是否存在,存在則返回。)如果要讓web服務器處理動態資源的請求,則需要使用cgi程序、組件加容器的方式。

    2)組件(可以單獨部署的軟件模塊,組件必須要符合相應的規範。)

      優點是可以加快軟件開發的速度,提高軟件的可維護性。容器:為組件提供運行環境,並且管理組件的生命周期。組件並不完全依賴特定的容器,只要符合相應的規範就可以。

  補充2:   

    Servlet和普通java類的區別:       客戶不能直接創建Servlet對象和調用Servlet的方法,只能通過向Web服務器發出HTTP請求,間接調用Servlet的方法。 

1.2、實現Servlet的方式 

  實現javax.servlet.Servlet接口
  繼承javax.servlet.GenericServlet類
  繼承javax.servlet.http.HttpServlet類

  通常會去繼承HttpServlet類來完成Servlet

1.3、Servlet與線程安全問題

  一個類型的Servlet只有一個實例對象,那麽就有可能會出現一個Servlet同時處理多個請求,線程不安全,但Servlet工作效率高。

  解決方案:    

    不要在Servlet中創建成員,創建局部變量變量即可!
    可以創建無狀態成員!
    可以創建有狀態的成員,但狀態必須位為只讀的! 

技術分享
public class Servlet extends HttpServlet {
    //無狀態成員
    /*public class User {
        public void hello() {
            System.out.println("Hello");
        }
    }*/
    //創建有狀態的成員,但狀態必須位為只讀的
    /*public class User {
        private String name = "zhangsan";
        public String getName() {
            return name;
        }
    }*/
    private User user = new User();

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doPost()...");
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doGet()...");
    }
}
示例代碼

二、tomcat和Servlet的聯系

  Tomcat 是Web應用服務器,是一個Servlet/JSP容器. Tomcat 作為Servlet容器,負責處理客戶請求,把請求傳送給Servlet,並將Servlet的響應傳送回給客戶.而Servlet是一種運行在支持Java語言的服務器上的組件.。

  Servlet最常見的用途是擴展Java Web服務器功能,提供非常安全的,可移植的,易於使用的CGI替代品。

  從http協議中的請求和響應可以得知,瀏覽器發出的請求是一個請求文本,而瀏覽器接收到的也應該是一個響應文本。

  但是在上面這個圖中,並不知道是如何轉變的,只知道瀏覽器發送過來的請求也就是request,我們響應回去的就用response。忽略了其中的細節,現在就來探究一下。

  技術分享

  1)Tomcat將http請求文本接收並解析,然後封裝成HttpServletRequest類型的request對象,所有的HTTP頭數據讀可以通過request對象調用對應的方法查詢到。

  2)Tomcat同時會要響應的信息封裝為HttpServletResponse類型的response對象,通過設置response屬性就可以控制要輸出到瀏覽器的內容,然後將response交給tomcat,tomcat就會將其變成響應文本的格式發送給瀏覽器。

  Java Servlet API 是Servlet容器(tomcat)和servlet之間的接口,它定義了serlvet的各種方法,還定義了Servlet容器傳送給Servlet的對象類,其中最重要的就是ServletRequest和ServletResponse。

  所以說我們在編寫servlet時,需要實現Servlet接口,按照其規範進行操作。

三、編寫一個簡單的Servlet

3.1、手動編寫一個Servlet

  1)創建一個Servlet_demo_0010繼承HttpServlet,重寫doGet和doPost方法,也就是看請求的方式是get還是post,然後用不同的處理方式來處理請求。

    技術分享

  2)在web.xml中配置Servlet_demo_0010,為什麽需要配置?讓瀏覽器發出的請求知道到達哪個servlet,也就是讓tomcat將封裝好的request找到對應的servlet讓其使用。

    技術分享

  分析:

    按照步驟,首先瀏覽器通過http://localhost:4040/Web_Servlet/ServletTest來找到web.xml中的url-pattern,這就是第一步,匹配到了url-pattern後,就會找到第二步servlet的名字MyServlet,知道了名字,

    就可以通過servlet-name找到第三步,到了第三步,也就能夠知道servlet的位置了。然後到其中找到對應的處理方式進行處理。

  3)測試

    技術分享

    技術分享說明配置成功了!(輸出get因為http請求默認是get請求)

3.2、使用Eclipse向導創建一個Servlet

  這個就相對簡單了,web.xml不用我們手動配置,工具直接幫我們自動配置了

  1)右擊項目,在new選項中有直接新建servlet的選項

  2)配置Servlet_demo_0020類中的信息

    技術分享

  3)配置web.xml中的servlet信息

    技術分享

  4)查看Servlet_demo_0020類中的代碼和web.xml,其中的配置跟手動的配置是一樣的,只是用圖形化界面,讓我們更方便的創建servlet而產生的。

四、Servlet生命周期

4.1、生命周期方法與特性

  void init(ServletConfig servletConfig):Servlet對象創建之後馬上執行的初始化方法,只執行一次;
  void service(ServletRequest servletRequest, ServletResponse servletResponse):每次處理請求都是在調用這個方法,它會被調用多次;
  void destroy():在Servlet被銷毀之前調用,負責釋放Servlet對象占用的資源的方法;

  特性:  

    單例,一個類只有一個對象,當然可能存在多個Servlet類
    線程不安全的,所以它的效率高。

  Servlet類由自己編寫,但對象由服務器來創建,並由服務器來調用相應的方法 

4.2、Servlet生命周期 

  服務器啟動時(web.xml中配置load-on-startup=1,默認為0)或者第一次請求該servlet時,就會初始化一個Servlet對象,也就是會執行初始化方法init(ServletConfig conf)

  該servlet對象去處理所有客戶端請求,在service(ServletRequest req,ServletResponse res)方法中執行

  最後服務器關閉時,才會銷毀這個servlet對象,執行destroy()方法。

  圖解:

    技術分享

  詳細說明:

    技術分享

  總結(面試會問):   

  1)Servlet何時創建
    默認第一次訪問servlet時創建該對象(調用init()方法)
  2)Servlet何時銷毀
    服務器關閉servlet就銷毀了(調用destroy()方法)
  3)每次訪問必須執行的方法
    public void service(ServletRequest arg0, ServletResponse arg1)

五、Servlet原理

5.1、Servlet執行過程

  在瀏覽器的地址欄輸入:http://ip:port/appNames/servlet

  1)通過瀏覽器和ip:port和這個服務器建立連接。
  2) 瀏覽器會生成一個請求數據包(路徑appNames/servlet)向服務器發送請求
  3) 服務器收到請求數據包,分析請求資源路徑做精準定位,通過請求的appName查找webapps文件下面的appName做匹配,匹配上了需要獲取web.xml中的servlet(mapping)。 
  4) 服務器創建兩個對象:
    第一個對象:請求對象,該對象實現了HttpServletRequest接口,服務器會將請求數據包中的數據解析出來,存儲在該對象裏。這樣做的好處是沒有必要理解http協議,只需要讀取request。
    第二個對象:響應對象,實現了HttpServletResponse接口,作用是servlet處理完成後的結果可以存放到該對象上,然後服務器依據該對象的數據生成響應數據包。
  5) servlet在執行servlet()方法時,可以通過request獲取請求數據,也可以將處理結果存放到response上。然後服務器與響應對象直接形成一個默契,生成一個響應數據包給瀏覽器。
  6)瀏覽器解析服務器返回的響應數據包,生成響應的結果。

  技術分享  

  Servlet訪問的過程:     Http請求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(對應的Class文件)

5.2、Servlet配置

  1)基本配置web.xml文件
    <!--servlet的類的配置-->
    <!--servlet的虛擬路徑的配置-->
   其中url-pattern的配置方式:
    1)完全匹配:訪問的資源與配置的資源完全相同才能訪問到     

      絕對地址只能映射到1個地址
      格式:/目錄/目錄/文件名.擴展名

      <url-pattern>/quickStartServlet</url-pattern>
    2)目錄匹配:格式:/虛擬的目錄..../*    

      格式:/目錄/目錄/*       這類映射重點匹配目錄,只要目錄符合映射模式,不考慮文件名,這個Servlet可以響應多個請求URL。

      <url-pattern>/aaa/bbb/ccc/*</url-pattern> //* 代表任意
    3)匹配擴展名:格式:*.擴展名;    

      格式:*.擴展名       以匹配擴展名的方式進行URL映射,不考慮文件的目錄信息,也可以響應多地址的請求。

      <url-pattern>*.abcd</url-pattern>

    註意:第二種與第三種混用 如 :/aaa/bbb/*.cba (錯誤)   

  2)服務器啟動實例化Servlet配置
    Servlet何時創建:默認第一次訪問時創建
    為什麽是默認?
    當在Servlet的配置是,加上一個配置<load-onstartup> ;
    servlet對象在服務器啟動時就創建。<!--數字代表優先級,數字越小優先級越高-->
    <load-on-startup>4</load-on-startup> 最好取中間數字 4/5。

  3)缺省Servlet  

    可以將url-pattern 配置一個/,代表該servlet是缺省的servlet。     什麽是缺省的servlet?       當你訪問資源地址所有的servlet都不匹配時,缺省的servlet賦值處理。     其實,web應用中所有的資源的響應都是servlet負責,包括靜態資源(html頁面)。(有配置缺省的servlet,無法訪問到靜態資源。)

5.3、創建的servlet是繼承自httpServlet,而不是直接實現Servlet接口的原理

  servlet的生命周期中,可以看出,執行的是service方法,為什麽我們就只需要寫doGet和doPost方法呢?   查看源碼,httpServlet的繼承結構   httpServlet繼承GenericServlet。懂的人立馬就應該知道,GenericServlet(通用Servlet)的作用是什麽?大概的就是將實現Servlet接口的方法,簡化編寫servlet的步驟。具體下面詳解       技術分享

  GenericServlet的繼承結構,實現了Servlet接口和ServletConfig接口

      技術分享

  Servlet接口內容

      技術分享

  從這裏可以看到,Servlet生命周期的三個關鍵方法,init、service、destroy。還有另外兩個方法,一個getServletConfig()方法來獲取ServletConfig對象

  ServletConfig對象可以獲取到Servlet的一些信息,ServletName、ServletContext、InitParameter、InitParameterNames、通過查看ServletConfig這個接口就可以知道:

  ServletConfig接口內容

      技術分享

  其中ServletContext對象是servlet上下文對象,功能有很多,獲得了ServletContext對象,就能獲取大部分我們需要的信息,比如獲取servlet的路徑,等方法

  到此,就知道了Servlet接口中的內容和作用,總結起來就是,三個生命周期運行的方法,獲取ServletConfig,而通過ServletConfig又可以獲取到ServletContext。而GenericServlet實現了Servlet接口後,

  也就說明我們可以直接繼承GenericServlet,就可以使用上面我們所介紹Servlet接口中的那幾個方法了,能拿到ServletConfig,也可以拿到ServletContext,不過那樣太麻煩,不能直接獲取ServletContext,

  所以GenericServlet除了實現Servlet接口外,還實現了ServletConfig接口,那樣,就可以直接獲取ServletContext了。

  GenericServlet類的內容詳解

      技術分享

  看上圖,用紅色框框起來的就是實現Servlet和ServletConfig接口所實現的方法,有9個,這很正常,但是我們可以發現,init方法有兩個,一個是帶有參數ServletConfig的,一個有無參的方法,為什麽這樣設計?

  這裏需要知道其中做了什麽事情,來看看這兩個方法分別做了什麽事?

    init(ServletConfig config)

      技術分享

    init()

      技術分享

    一個成員變量config

      技術分享

    getServletConfig()

      技術分享

  通過這幾個方法一起來講解,首先看init(ServletConfig config)方法,因為只有init(ServletConfig config)中帶有ServletConfig對象,為了方便能夠在其他地方也能直接使用ServletConfig對象,而不僅僅局限在init(ServletConfig config)方法中,

  所以創建一個私有的成員變量config,在init(ServletConfig config)方法中就將其賦值給config,然後通過getServletConfig()方法就能夠獲取ServletConfig對象了,這個可以理解,但是在init(ServletConfig config)中,158行,還調用了一個init()方法,

  並且這個init()方法是空的,什麽讀沒有,這是為什麽呢?這個原因是為了防止一件事情,當我們需要在init方法中做一點別的事情,我們想到的方法就是繼承GenericServlet並且重寫了init(ServletConfig config)方法,這樣依賴,

  就破壞了原本在GenericServlet類中init(ServletConfig config)寫的代碼了,也就是在GenericServlet類中的成員變量config會一直是null,無法得到賦值,因為被重寫了,就不會在執行GenericServlet中init(ServletConfig config)方法中的代碼。

  要想賦值,就必須在重寫的init(ServletConfig config)方法中調用父類的init(ServletConfig config)方法,也就是super.init(ServletConfig config),這樣一來,就很不方便,怕有時候會忘了寫這句代碼,所以在GenericServlet類中增加一個init()方法,

  以後需要在init方法中需要初始化別的數據,只需要重寫init()這個方法,而不需要去覆蓋init(ServletConfig config)這個方法,這樣設計,就好很多,不用在管init(ServletConfig config)這個其中的內容了。也不用出現其他的問題。

  service(ServletRequest req, ServletResponse res)

      技術分享

  一個抽象方法,說明在GenericServlet類中並沒有實現該內容,那麽我們想到的是,在它上面肯定還有一層,也就是還有一個子類繼承它,實現該方法,要是讓我們自己寫的Servlet繼承GenericServlet,

  需要自己寫service方法,那豈不是累死,並且我們可以看到,service方法中的參數還是ServletRequest,ServletResponse。並沒有跟http相關對象掛鉤,所以我們接著往下面看。

  HttpServlet類詳解

    繼承了GenericServlet類,通過我們上面的推測,這個類主要的功能肯定是實現service方法的各種細節和設計。並且通過類名可以知道,該類就跟http掛鉤了。

      技術分享

    關註service(HttpServletRequest req, HttpServletResponse resp)方法和service(ServletRequest req, ServletResponse res)方法。

    service(ServletRequest req, ServletResponse res)方法

      技術分享

    該方法中就做一件事情,就是將ServletRequest和ServletResponse這兩個對象強轉為HttpServletRequest和HttpServletResponse對象。為什麽能這樣轉?

    首先要知道req、res是什麽類型,通過打印System.out.println(req),可以知道,req實際上的類型是org.apache.catalina.connector.RequestFacade

    Tomcat中的源碼:

      技術分享   

    通過圖可以得知,req的繼承結構:RequestFacade、httpServletRequest、ServletRequest,我們知道本身req是ServletRequest,那麽從繼承結構上看,它也可以看成HttpServletRequest,也可以看成ServletRequest,

    所以強轉為HttpServletRequest是可以的,如果不明白,我舉個例子,ArrayList、List、Object 這個,Object obj = new ArrayList(); List list = new ArrayList(); 一個ArrayList對象可以看成List對象, 也可以看成一個Object對象,

    現在obj是不是可以堪稱List對象呢?答案是可以的,因為obj就是ArrayList對象,既然是ArrayList對象,那麽就可以看成是List對象。一樣的道理,RequestFacade 對應 ArrayList、httpServleRequest對應 List、 ServletRequest 對應 Object。

    轉換為httpServletRequest和HttpServletResponse對象之後,在調用service(HttpServletRequest req, HttpServletResponse resp)方法。

    service(HttpServletRequest req, HttpServletResponse resp)

    這個方法就是判斷瀏覽器過來的請求方式是哪種,每種的處理方式不一樣,我們常用的就是get,post,並且,我們處理的方式可能有很多的內容,所以,在該方法內會將get,post等其他5種請求方式提取出來,

    變成單個的方法,然後我們需要編寫servlet時,就可以直接重寫doGet或者doPost方法就行了,而不是重寫service方法,更加有針對性。所以這裏就回到了我們上面編寫servlet時的情況,繼承httpServlet,

    而只要重寫兩個方法,一個doGet,一個doPost,其實就是service方法會調用這兩個方法中的一個(看請求方式)。所

喜歡就點個“推薦”哦!

JavaWeb(一)之細說Servlet