Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

Java3y發表於2019-03-01

Servlet的呼叫圖

前面我們已經學過了Servlet的生命週期了,我們根據Servlet的生命週期畫出Servlet的呼叫圖加深理解

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

Servlet的細節

一個已經註冊的Servlet可以被多次對映

同一個Servlet可以被對映到多個URL上。


	    <servlet>
	        <servlet-name>Demo1</servlet-name>
	        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
	    </servlet>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>/Demo1</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>/ouzicheng</url-pattern>
	    </servlet-mapping>

複製程式碼

無論我訪問的是http://localhost:8080/Demo1還是http://localhost:8080/ouzicheng。我訪問的都是Demo1。

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

Servlet對映的URL可以使用萬用字元

萬用字元有兩種格式:

  1. *.副檔名
  2. 正斜槓(/)開頭並以“/*”結尾。

匹配所有

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

匹配副檔名為.jsp的

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

如果*.副檔名和正斜槓(/)開頭並以“/*”結尾兩種萬用字元同時出現,匹配的是哪一個呢?

  1. 看誰的匹配度高,誰就被選擇
  2. *.副檔名的優先順序最低

Servlet對映的URL可以使用萬用字元和Servlet可以被對映到多個URL上的作用:

  1. 隱藏網站是用什麼程式語言寫的【.php,.net,.asp實際上訪問的都是同一個資源】
  2. 用特定的字尾宣告版權【公司縮寫】

		 <servlet>
	        <servlet-name>Demo1</servlet-name>
	        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
	    </servlet>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.jsp</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.net</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.asp</url-pattern>
	    </servlet-mapping>
	    <servlet-mapping>
	        <servlet-name>Demo1</servlet-name>
	        <url-pattern>*.php</url-pattern>
	    </servlet-mapping>



複製程式碼

Servlet是單例的

為什麼Servlet是單例的

瀏覽器多次對Servlet的請求,一般情況下,伺服器只建立一個Servlet物件,也就是說,Servlet物件一旦建立了,就會駐留在記憶體中,為後續的請求做服務,直到伺服器關閉

每次訪問請求物件和響應物件都是新的

對於每次訪問請求,Servlet引擎都會建立一個新的HttpServletRequest請求物件和一個新的HttpServletResponse響應物件,然後將這兩個物件作為引數傳遞給它呼叫的Servlet的service()方法service方法再根據請求方式分別呼叫doXXX方法

執行緒安全問題

當多個使用者訪問Servlet的時候,伺服器會為每個使用者建立一個執行緒當多個使用者併發訪問Servlet共享資源的時候就會出現執行緒安全問題

原則:

  1. 如果一個變數需要多個使用者共享,則應當在訪問該變數的時候,加同步機制synchronized (物件){}
  2. 如果一個變數不需要共享,則直接在 doGet() 或者 doPost()定義.這樣不會存線上程安全問題

load-on-startup

如果在元素中配置了一個元素,那麼WEB應用程式在啟動時,就會裝載並建立Servlet的例項物件、以及呼叫Servlet例項物件的init()方法

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

作用:

  1. 為web應用寫一個InitServlet,這個servlet配置為啟動時裝載,為整個web應用建立必要的資料庫表和資料
  2. 完成一些定時的任務【定時寫日誌,定時備份資料】

在web訪問任何資源都是在訪問Servlet

當你啟動Tomcat,你在網址上輸入http://localhost:8080。為什麼會出現Tomcat小貓的頁面?

這是由預設Servlet為你服務的

  • 我們先看一下web.xml檔案中的配置,web.xml檔案配置了一個預設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>


複製程式碼
  • 什麼叫做預設Servlet?凡是在web.xml檔案中找不到匹配的元素的URL,它們的訪問請求都將交給預設Servlet處理,也就是說,預設Servlet用於處理所有其他Servlet都不處理的訪問請求
  • 既然我說了在web訪問任何資源都是在訪問Servlet,那麼我訪問靜態資源【本地圖片,本地HTML檔案】也是在訪問這個預設Servlet【DefaultServlet】
  • 證實一下:當我沒有手工配置預設Servlet的時候,訪問本地圖片是可以訪問得到的

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 現在我自己配置一個預設Servlet,Demo1就是我手工配置的預設Servlet,覆蓋掉web.xml配置的預設Servlet

    <servlet>
        <servlet-name>Demo1</servlet-name>
        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Demo1</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


複製程式碼
  • 下面我繼續訪問一下剛才的圖片,此時輸出的是Demo1這個Servlet寫上的內容了

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 總結:**無論在web中訪問什麼資源【包括JSP】,都是在訪問Servlet。**沒有手工配置預設Servlet的時候,你訪問靜態圖片,靜態網頁,預設Servlet會在你web站點中尋找該圖片或網頁,如果有就返回給瀏覽器,沒有就報404錯誤

ServletConfig物件

ServletConfig物件有什麼用?

通過此物件可以讀取web.xml中配置的初始化引數。

現在問題來了,為什麼我們要把引數資訊放到web.xml檔案中呢?我們可以直接在程式中都可以定義引數資訊,搞到web.xml檔案中又有什麼好處呢

好處就是:能夠讓你的程式更加靈活【更換需求,更改配置檔案web.xml即可,程式程式碼不用改】

獲取web.xml檔案配置的引數資訊

  • 為Demo1這個Servlet配置一個引數,引數名是name,值是zhongfucheng

    <servlet>
        <servlet-name>Demo1</servlet-name>
        <servlet-class>zhongfucheng.web.Demo1</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>zhongfucheng</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo1</servlet-name>
        <url-pattern>/Demo1</url-pattern>
    </servlet-mapping>

複製程式碼
  • 在Servlet中獲取ServletConfig物件,通過ServletConfig物件獲取在web.xml檔案配置的引數
    Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

ServletContext物件

什麼是ServletContext物件?

當Tomcat啟動的時候,就會建立一個ServletContext物件。它代表著當前web站點

ServletContext有什麼用?

  1. ServletContext既然代表著當前web站點,那麼所有Servlet都共享著一個ServletContext物件,所以Servlet之間可以通過ServletContext實現通訊
  2. ServletConfig獲取的是配置的是單個Servlet的引數資訊,ServletContext可以獲取的是配置整個web站點的引數資訊
  3. 利用ServletContext讀取web站點的資原始檔
  4. 實現Servlet的轉發【用ServletContext轉發不多,主要用request轉發】

Servlet之間實現通訊

ServletContext物件可以被稱之為域物件

到這裡可能有一個疑問,域物件是什麼呢?其實域物件可以簡單理解成一個容器【類似於Map集合】

實現Servlet之間通訊就要用到ServletContext的setAttribute(String name,Object obj)方法, 第一個引數是關鍵字,第二個引數是你要儲存的物件

  • 這是Demo2的程式碼

        //獲取到ServletContext物件
        ServletContext servletContext = this.getServletContext();

        String value = "zhongfucheng";

        //MyName作為關鍵字,value作為值存進   域物件【型別於Map集合】
        servletContext.setAttribute("MyName", value);


複製程式碼
  • 這是Demo3的程式碼

        //獲取ServletContext物件
        ServletContext servletContext = this.getServletContext();

        //通過關鍵字獲取儲存在域物件的值
        String value = (String) servletContext.getAttribute("MyName");

        System.out.println(value);

複製程式碼
  • 訪問Demo3可以獲取Demo2儲存的資訊,從而實現多個Servlet之間通訊

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】


獲取web站點配置的資訊

如果我想要讓所有的Servlet都能夠獲取到連線資料庫的資訊,不可能在web.xml檔案中每個Servlet中都配置一下,這樣程式碼量太大了!並且會顯得非常囉嗦冗餘。

  • web.xml檔案支援對整個站點進行配置引數資訊所有Servlet都可以取到該引數資訊

    <context-param>
        <param-name>name</param-name>
        <param-value>zhongfucheng</param-value>
    </context-param>

複製程式碼
  • Demo4程式碼

        //獲取到ServletContext物件
        ServletContext servletContext = this.getServletContext();

        //通過名稱獲取值
        String value = servletContext.getInitParameter("name");
        System.out.println(value);


複製程式碼

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 試一下Demo3是否能拿到,相同的程式碼

        //獲取到ServletContext物件
        ServletContext servletContext = this.getServletContext();

        //通過名稱獲取值
        String value = servletContext.getInitParameter("name");
        System.out.println(value);

複製程式碼

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】


讀取資原始檔

第一種方式:

  • 現在我要通過Servlet111讀取1.png圖片

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 按我們以前的方式,程式碼應該是這樣的。

        FileInputStream fileInputStream = new FileInputStream("1.png");
        System.out.println(fileInputStream);

複製程式碼
  • 當我們訪問的時候,卻出錯了!說找不到1.png檔案

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 這是為什麼呢?我們以前讀取檔案的時候,如果程式和檔案在同一包名,可以直接通過檔名稱獲取得到的!,原因很簡單,以前我們寫的程式都是通過JVM來執行的,而現在,我們是通過Tomcat來執行的
  • 根據web的目錄規範,Servlet編譯後的class檔案是存放在WEB-INF\classes資料夾中的

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 看到這裡,我們知道了要進入classes目錄中讀取檔案,所以我們將程式碼改成以下方式


        FileInputStream fileInputStream = new FileInputStream("D:\\zhongfucheng\\web\\WEB-INF\\classes\\zhongfucheng\\web\\1.png");
        System.out.println(fileInputStream);


複製程式碼
  • 再去讀取時,就發現可以獲取到檔案了。
  • 但是現在問題又來了,我讀取檔案的時候都要寫上絕對路徑,這樣太不靈活了。試想一下,如果我將該讀取檔案的模組移到其他的web站點上我的程式碼就又要修改了【因為web站點的名字不一樣】
  • 我們通過ServletContext讀取就可以避免修改程式碼的情況,因為ServletContext物件是根據當前web站點而生成的
  • 程式碼如下所示:

        //獲取到ServletContext物件
        ServletContext servletContext = this.getServletContext();

        //呼叫ServletContext方法獲取到讀取檔案的流
        InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/classes/zhongfucheng/web/1.png");

複製程式碼

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】


第二種方式:

  • 如果我的檔案放在web目錄下,那麼就簡單得多了!,直接通過檔名稱就能獲取

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 程式碼如下所示


        //獲取到ServletContext物件
        ServletContext servletContext = this.getServletContext();

        //呼叫ServletContext方法獲取到讀取檔案的流
        InputStream inputStream = servletContext.getResourceAsStream("2.png");

複製程式碼

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】


第三種方式:

通過類裝載器讀取資原始檔

  • 我的檔案放在了src目錄下【也叫做類目錄】

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 程式碼如下所示

        //獲取到類裝載器
        ClassLoader classLoader = Servlet111.class.getClassLoader();

        //通過類裝載器獲取到讀取檔案流
        InputStream inputStream = classLoader.getResourceAsStream("3.png");



複製程式碼

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 我的檔案放在了src目錄下的包下

Servlet第二篇【Servlet呼叫圖、Servlet細節、ServletConfig、ServletContext】

  • 程式碼如下,新增包名路徑即可。

        //獲取到類裝載器
        ClassLoader classLoader = Servlet111.class.getClassLoader();

        //通過類裝載器獲取到讀取檔案流
        InputStream inputStream = classLoader.getResourceAsStream("/zhongfucheng/web/1.png");



複製程式碼

原則:如果檔案太大,就不能用類裝載器的方式去讀取,會導致記憶體溢位


如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y

相關文章