1. 程式人生 > >深入理解web.xml中配置/和/*的區別

深入理解web.xml中配置/和/*的區別

 在用SpringMVC進行web開發的時候,如果將DispathcerServlet對外訪問的虛擬路徑配置成/時,需要在Spring的配置檔案中配置<mvc:default-servlet-handler/>這一項,那麼為什麼需要配置這一項呢?如果對外訪問虛擬路徑配置成/*可以嗎?接下來本文將帶著這樣的兩個疑問,從Tomcat中的conf/web.xml檔案著手,深入淺出的分析這兩種配置的真正區別。

1. 一個注意點:why?攔截why?匹配
       在描述一個Servlet對應的url-patten標籤時,很多人會用“攔截”一詞來描述這種關係,其實“攔截”一詞用在這是很不貼切的。在日常生活中,“攔截”一詞顧名思義就是攔下來,不給它通行權。那麼用在Servlet中應該就是把請求給攔下來,不給瀏覽器響應。實際上,這顯然不是我們要的結果。那麼該用什麼詞來更為妥當的描述這種關係呢?


è¿éåå¾çæè¿°

上圖是截自Tomact中conf/web.xml中的org.apache.jasper.servlet.JspServlet(第113行)說明,從中可以看到是用map一詞來描述這種關係的。map譯為“對映”,但”對映”過於專業化,對於初學者來說不易理解,因此使用意思貼近的”匹配”可以更好的描述這種關係,更為平易近人。因此只有請求匹配到了相應的Servelt,伺服器才會給出響應,否則對不起,給出404.
 

2. 一點說明:所有的請求資源都需要經過Servlet處理
       所有的請求資源都需要經過Servlet處理,包括但不限於靜態資源(.html、.css、jpg等檔案)、.jsp請求和Servlet請求。初學web時,在位址列直接訪問靜態資源(localhost:8080/index.html)的時候,給我們的感覺是不需要經過Servlet處理的,伺服器會直接給我們做出響應。其實不然,在Tomcat中的conf/web.xml有如下說明(第38行),這段說明是關於org.apache.catalina.servlets.DefaultServlet的。
 

è¿éåå¾çæè¿°

  從中可以看到DefaultServlet主要處理的是靜態資源,這個Servlet處理規則是其它Servlet不能匹配的請求將由它進行處理。 而在Tomcat中的DefalutServlet(預設Servlet、預設Servlet)的路徑配置恰巧是/。

è¿éåå¾çæè¿°

     如果在自己寫的web應用中,同樣配置一個路徑/,則會覆蓋Tomcat中的conf/web.xml中的預設Servlet,為此在自己寫的web.xml中配置如下:

  <servlet>
    <servlet-name>DefaultServlet</servlet-name>
    <servlet-class>cn.tedu.test.DefaultServlet</servlet-class>
  </servlet>

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

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>

 在自己寫的DefaultServlet中有如下程式碼:

 //告訴瀏覽器響應的是文字資訊
        response.setContentType("text/html;charset=utf-8");
        //獲得index.html的真實路徑
        String path = this.getServletContext().getRealPath("index.html");
        //獲得輸入輸出流,並將檔案內容響應給瀏覽器
        FileInputStream fis = new FileInputStream(path);
        OutputStream out = response.getOutputStream();
        int i = -1;
        while((i = fis.read()) != -1){
            out.write(i);
        }

準備一個簡單的html靜態資源放在webRoot目錄下:

<!DOCTYPE html>
    <html>
      <head>
        <title>index.html</title>
        <meta charset="UTF-8">
      </head>

      <body>
        This is my HTML page. <br>
      </body>
    </html>

這個時候在瀏覽器輸入相應的地址,瀏覽器會有”This is my HTML page.”顯示,表明此時可以正確訪問靜態資源index.html。當將自己的DefalutServlet中的java程式碼給註釋掉後,再從瀏覽器訪問index.html靜態資源,會發現瀏覽器不會做出任何響應。從這個結果中可以看到靜態資源是由Servlet以流的形式響應給頁面的,在這個Servlet中有一個重要的設定response.setContentType(“text/html;charset=utf-8”)。這個設定是告訴瀏覽器伺服器將傳送的是text/html資訊,瀏覽器將會按照這種格式去解析瀏覽器所傳送的內容(ig.傳送的是png格式的圖片則為image/png)。 
       從上我們可以看到當在自己的Servlet中配置/的時候會覆蓋Tomcat中config/web.xml中的/配置,這時如果我們不對靜態資源進行相應的處理的時候,將導致靜態資源無法訪問。可見,Tomcat中的config/web.xml中的預設Servlet主要處理的就是靜態資源的訪問。 
       這也是為什麼平常我們在自己的web應用中沒有處理配置處理靜態資源ervlet的時候,靜態資源仍然可以正常訪問和顯示的原因。因此當我們在springMVC中將DispatcherServlet配置成/時,我們一定要在spring的配置檔案中加上如下配置的原因。
 

<!-- 處理靜態資源被"/"所攔截的問題 -->
    <mvc:default-servlet-handler/>

3. /和/*區別
       其實/和/*都可以匹配所有的請求資源,但其匹配的優先順序是不同的。/在所有的匹配路徑中,優先順序最低,即當別的路徑都無法匹配時,/所匹配的預設Servlet才會進行相應的請求資源處理。而 /星號 匹配的優先順序是高於/路徑和星號.字尾的路徑的(如星號.action,星號.jsp等路徑)。 
       為了更好的說明這個問題我們以一個例子來說明。相關檔案如下(注意匯入springMVC的jar包):
è¿éåå¾çæè¿°

web.xml配置如下:

<servlet>
      <servlet-name>springmvc</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
  </servlet>

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

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>!

springmvc-servlet.xml配置如下:

 <!-- 處理靜態資源被"/"所攔截的問題 -->
    <mvc:default-servlet-handler/>

 index.jsp檔案如下:

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE >
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>

  <body>
    This is my JSP page. <br>
    <img alt="圖片" src="1.jpg" border="1px" width="400px" height="200px">
  </body>
</html>

在瀏覽器位址列輸入localhost:8080/hello,我們發現頁面文字和圖片內容都可以正常顯示。

è¿éåå¾çæè¿°

 當將springmvc-servlet.xml中關於靜態資源訪問的配置給註釋掉後,再次訪問結果如下:

è¿éåå¾çæè¿°

   這時靜態的圖片資源無法訪問,這正如一開始論述的結論一樣。這時我們再次開啟springmvc-servlet.xml中關於靜態資源的配置,並將web.xml中對外訪問的虛擬路徑配置成/*這樣的形式。再次訪問會丟擲一個404的錯誤。而將位址列改為localhost:8080/hello/index.jsp強行訪問該資源的時候,會出現以下結果:
è¿éåå¾çæè¿°

   即將jsp內容以文字的形式顯示出來了。接著我們繼續將springmvc-servlet.xml中關於靜態資源的訪問配置給註釋掉,繼續訪問localhost:8080/hello/index.jsp資源,這時會丟擲一個404錯誤,也就是說在上一次訪問的時候,伺服器把index.jsp當成了靜態資源響應給了瀏覽器,所有我們才會看到這樣的文字頁面。 
       那麼為什麼在配置成/*後index.jsp就不能正常訪問了?原來在Tomcat中conf/web.xml中配置了org.apache.jasper.servlet.JspServlet用來處理.jsp這樣的資源了(第113行)。
è¿éåå¾çæè¿°

這段話的意思是說JspServlet是jsp頁面的編譯和執行的Servlet.也就是說JspServlet可以將.jsp頁面轉換成.java檔案,最終編譯成.class檔案並執行。 
       至此,對/和/*的配置有一個這樣的總結:兩者都可以匹配任何資源,只不過兩者的匹配的優先順序是不一樣的;當請求資源與其它Servlet都無法匹配的時候,/所配置的Servlet才會匹配。 
       在Tomcat中conf/web.xml中有關於/和.jsp頁面處理的Servlet,當自己所配的web.xml檔案中配置/時,會使Tomcat中的DefaultServlet無法訪問,導致靜態資源無法訪問,因此在SpingMVC配置檔案中要開啟處理靜態資源的開關。實際上,當將對外訪問的虛擬路徑配置成*.action等類似路徑的時候,就可以很好的避免上述問題了。

4. 引申一:Servlet對外訪問虛擬路徑匹配順序簡介
       Servlet對外訪問的虛擬路徑的匹配順序大致如下:

       1.含有全部或部分對外訪問的具體路徑配置,如/LoginServlet,/servlet/*(優先匹配更為具體的路徑); 
       2./*的配置; 
       3.形如*.action這樣字尾形式的配置,如index.jsp,login.action(即使是具體的路徑,優先順序也會低於1和2的配置); 
       4.其它配置無法匹配的時候,匹配/所配置的Servlet。

       注意:/servlet/*.action這樣的配置是錯誤的,即不應該存在這樣的配置。

5. 引申二:過濾器對外訪問虛擬路徑可以配置成/嗎?
       答案是否定的。 
       在Servlet中,請求資源必須要有相應的Servlet進行處理,否則會丟擲404錯誤。但在過濾器中則不一樣,請求資源有時候並不需要經過過濾器的處理,因此當配置成/時,過濾器將不會生效。這與/配置的結論是相符的,即/只會匹配其它請求資源無法處理的請求,而在過濾器中對請求資源並不要求一定要處理。