JavaWeb之Servlet詳解

一、Servlet概述

  1.sun公司提供的動態web資源開發技術。本質是上一段java小程序,要求這個小程序必須實現Servlet接口,以便服務器能夠調用

  2.開發Servlet的兩個步驟
        *實驗:Servlet的快速入門
            (1)步驟一:寫一個java程序實現Servlet接口(此處直接繼承了默認實現類GenericServlet)
              

                package cn.itheima;
                import java.io.*;
                import javax.servlet.*;
                
                public class FirstServlet extends GenericServlet{
                    public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{
                            res.getOutputStream().write("My FirstServlet!".getBytes());
                    }
                
                }


                
            (2)將編譯好的帶包的.class放到WEB-INF/classes下,並還要配置web應用的 web.xml註冊Servlet
             
 <servlet>
    <servlet-name>FirstServlet</servlet-name>               ------------>   FirstServlet:表示servlet的名稱
    <servlet-class>cn.itheima.FirstServlet</servlet-class>  ------>     FirstServlet的完整類名
</servlet>
<servlet-mapping>
   <servlet-name>FirstServlet</servlet-name>   ------->           FirstServlet:表示servlet的名稱,建議和<servlet>標籤中的servlet名稱一致
   <url-pattern>/FirstServlet</url-pattern>       ------->        表示FirstServlet的外部訪問路徑
</servlet-mapping>

二、Servlet的詳述

1.生命週期:一件事物什麼時候生,什麼時候死,在生存期間必然會做的事情,所有這些放在一起就是該事物的生命週期。
2.Servlet的生命週期:通常情況下,servlet第一次被訪問的時候在內存中創建對象,在創建後立即調用init()方法進行初始化。對於每一次請求都調用service(req,resp)方法處理請求,此時會用Request對象封裝請求信息,並用Response對象(最初是空的)代表響應消息,傳入到service方法裏供使用。當service方法處理完成後,返回服務器,此時服務器根據Response中的信息組織成響應消息返回給瀏覽器。響應結束後servlet對象並不銷燬,一直駐留在內存中等待下一次請求。直到服務器關閉或web應用被移出虛擬主機,servlet對象銷燬並在銷燬前調用destroy()方法做一些善後的事情。
3.Servlet接口的繼承結構
    Servlet接口:定義了一個servlet應該具有的方法,所有的Servlet都應該直接或間接實現此接口
    |
    |----GenericServlet:對Servlet接口的默認實現,通用Servlet,這是一個抽象類,其中的大部分方法都做了默認實現,只有service方法是一個抽象方法需要繼承者自己實現
                |
                |----HttpServlet:對HTTP協議進行了優化的Servlet,繼承自GenericServlet類,並且實現了其中的service抽象方法,默認的實現中判斷了請求的請求方式,並根據請求方式的不同分別調用不同的doXXX()方法。通常我們直接繼承HttpServlet即可


4.web.xml註冊Servlet的注意事項
        4.1利用<servlet><servlet-mapping>標籤註冊一個Servlet

  <servlet>
        <servlet-name>FirstServlet</servlet-name>
        <servlet-class>cn.itheima.FirstServlet</servlet-class>  注意:此處要的是一個Servlet的完整類名,不是包含.java或.class擴展的文件路徑
    </servlet>
    <servlet-mapping>
        <servlet-name>FirstServlet</servlet-name>
        <url-pattern>/FirstServlet</url-pattern>
    </servlet-mapping>


    4.2一個<servlet>可以對應多個<servlet-mapping>
    4.3可以用*匹配符配置<serlvet-mapping>,但是只能有兩種固定的格式:一種格式是“*.擴展名”,另一種格式是以正斜槓(/)開頭並以“/*”結尾。    

             
<servlet-mapping>   
            <servlet-name>
                AnyName
            </servlet-name>
            <url-pattern>
                *.do
            </url-pattern>
        </servlet-mapping>
        
        <servlet-mapping>
            <servlet-name>
                AnyName
            </servlet-name>
            <url-pattern>
                /action/*
            </url-pattern>
        </servlet-mapping>   


    ~由於匹配符的引入有可能一個虛擬路徑會對應多個servlet-mapping,此時哪個訪問路徑最匹配將啓動那個servlet,並且*.dos最低。
       4.4可以爲<servlet>配置<load-on-startup>子標籤,指定servlet隨着服務器的啓動而加載,其中配置的數值指定啓動的順序
        
  <servlet>
                <servlet-name>invoker</servlet-name>
                <servlet-class>
                    org.apache.catalina.servlets.InvokerServlet
                </servlet-class>
                <load-on-startup>2</load-on-startup>
            </servlet>


        4.5缺省servlet:如果一個servlet的對外訪問路徑被設置爲/,則該servlet就是一個缺省servlet,其他servlet不處理的請求都由它來處理
                ~在conf/web.xml中配置了缺省servlet,對靜態資源的訪問和錯誤頁面的輸出就是由這個缺省servlet來處理的。如果我們自己寫一個缺省servlet將覆蓋tomcat/conf目錄下web.xml中的缺省servlet覆蓋的話,會導致靜態web資源無法訪問。所以不推薦配置。
        4.6servlet的線程安全問題
                4.6.1由於通常情況下,一個servlet在內存只有一個實例處理請求,當多個客戶端同時訪問訪問該servlet對象(操作該servlet對象),此時可能導致線程安全問題。
                        (1)servlet的成員變量可能存在線程安全問題

                            *實驗:定義一個成員變量 int i = 0;在doXXX()方法中進行i++操作並輸出i值到客戶端,此時由於延遲可能導致線程安全問題
                        (2)servlet操作資源文件時,多個線程操作同一文件引發線程安全問題
                            *實驗:請求帶着一個參數過來,servlet將請求參數寫入到一個文件,再讀取該文件,將讀取到的值打印到客戶端上,有可能有線程安全問題
                            
                4.6.2解決方法
                        (1)利用同步代碼塊解決問題。缺陷是,同一時間同步代碼塊只能處理一個請求,效率很低下,所以同步代碼塊中儘量只包含核心的導致線程安全問題的代碼。
                        (2)爲該servlet實現SingleThreadModel接口,此爲一個標記接口,被標記的servlet將會在內存中保存一個servlet池,如果一個線程來了而池中沒有servlet對象處理,則創建一個新的。如果池中有空閒的servlet則直接使用。這並不能真的解決線程安全問題。此接口已經被廢棄。
                        (3)兩種解決方案都不夠完美,所以儘量不要在servlet中出現成員變量。
                        
                        
三、ServletConfig
    1.代表servlet配置的對象,可以在web.xml中<servlet>中配置
   
    <servlet>
        <servlet-name>Demo5Servlet</servlet-name>
        <servlet-class>cn.itheima.Demo5Servlet</servlet-class>
        <init-param>
            <param-name>data1</param-name>
            <param-value>value1</param-value>
        </init-param>
      </servlet>

      然後在servlet中利用this.getServletConfig()獲取ServletConfig對象,該對象提供了getInitParameter()和getInitParameterNames()方法,可以遍歷出配置中的配置項。
      不想在servlet中寫死的內容可以配置到此處。
    
    
四、ServletContext
    1.代表當前web應用的對象。
    
    2.作爲域對象使用
,在不同servlet之間傳遞數據,作用範圍是整個web應用
        ~域:一個域就理解爲一個框,這裏面可以放置數據,一個域既然稱作域,他就有一個可以被看見的範圍,這個範圍內都可以對這個域中的數據進行操作,那這樣的對象就叫做域對象。
    3.在web.xml可以配置整個web應用的初始化參數,利用ServletContext去獲得
 
  <context-param>
        <param-name>param1</param-name>
        <param-value>pvalue1</param-value>
    </context-param>
    this.getServletContext().getInitParameter("param1")
    this.getServletContext().getInitParameterNames()
   
    4.在不同servlet之間進行轉發
        this.getServletContext().getRequestDispatcher("/servlet/Demo10Servlet").forward(request, response);
        方法執行結束,service就會返回到服務器,再有服務器去調用目標servlet,其中request會重新創建,並將之前的request的數據拷貝進去。
    
    
    5.讀取資源文件
            5.1由於相對路徑默認相對的是tomcat服務啓動的目錄,所以我們直接寫相對路徑將會是相對於tomcat/bin目錄,所以是拿不到資源的。如果寫成絕對路徑,當項目發佈到其他環境時,絕對路徑就錯了。
            5.2爲了解決這個問題ServletContext提供了this.getServletContext().getRealPath("/1.properties"),給進一個資源的虛擬路徑,將會返回該資源在當前環境下的真實路徑。this.getServletContext().getResourceAsStream("/1.properties"),給一個資源的虛擬路徑返回到該資源真實路徑的流。
            5.3當在非servlet下獲取資源文件時,就沒有ServletContext對象用了,此時只能用類加載器
                classLoader.getResourceAsStream("../../1.properties"),此方法利用類加載器直接將資源加載到內存中,有更新延遲的問題,以及如果文件太大,佔用內存過大。
                classLoader.getResource("../1.properties").getPath(),直接返回資源的真實路徑,沒有更新延遲的問題。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章