天天看點

Servlet虛拟路徑映射詳解

​ 在上一篇中我們初識了Servlet,相信大家對Servlet也都有了些了解,知道了如何建立一個Servlet,并且為其添加虛拟映射,最終釋出項目,并在浏覽器上請求對應的Servlet。

​ 我們知道,隻有給Servlet配置好虛拟路徑,用戶端才可以進行通路,但是對于Servlet的路徑映射,真的隻有現在所知的這麼簡單麼?

​ 答案當時是No了,不然怎麼會有這篇文章????,下面讓我們一起來探究其中的秘密吧!

1.配置多個映射路徑

​ 在上一文中,我們說到@WebServlet中的urlPatterns屬性,其可以是一組比對規則,也就是說一個Servlet是可以配置多個虛拟路徑的,也就是Servlet和虛拟路徑可以是一對多的一個關系(并不是多對多,一個虛拟路徑隻能映射一個Servlet),其具體實作如下,并修改doPost處的代碼:

@WebServlet(
		description = "My First Servlet", 
		urlPatterns = { "/HelloServlet", "/StillMe" }, 
		initParams = { 
				@WebInitParam(name = "name", value = "lizishu")
		})
public class HelloServlet extends HttpServlet {
  //具體邏輯參看上篇文章
  //...
  
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//設定傳回用戶端的contentType
		//text/plain :純文字格式  設定為text/html println的換行會失效
		response.setContentType("text/plain;charset=utf-8");
		//response.setCharacterEncoding("utf-8"); 
		PrintWriter out = response.getWriter();
		out.println("Served at: " + request.getContextPath());
		String name = this.getInitParameter("name");
		out.println("name: " + name);
		out.println("通路的Servle名為:" + HelloServlet.class);
	}
}
           

​ 可以看到,增加一個虛拟路徑映射非常友善,隻需在urlPatterns中新增一項即可(注意’/'不可省略),啟動項目,在浏覽器上輸入url,可以看到,無論是輸入http://localhost:8080/FirstProject/HelloServlet、還是http://localhost:8080/FirstProject/StillMe頁面上得到的輸出内容均一緻。

​ urlPatterns在Servlet 3.0版本之前,都是配置在web.xml中的,每個Servlet會有一個對應的

<servlet-mapping>

标簽,其中可以配置多個

<url-pattern>

2.urlPatterns比對規則

​ 說到Servlet虛拟路勁的比對規則,還需要說到urlPatterns的幾種比對規則,主要有以下四種:

  • 精确比對:也就是我們在上面配置的比對規則,需要完全相等才能比對成功,這也是我們經常發生錯誤的地方,請求Servlet時的大小寫拼寫錯誤導緻404;
  • 路徑比對:比如想比對以rest開頭的所有請求,可以寫成"/rest/*",其格式為以’/‘字元開頭,并以’/*'結尾;
  • 擴充名比對:比如想比對所有以.do結尾的請求,可以寫成"*.do",其格式為以’*.’,後面跟上擴充名;
  • 預設比對:映射路徑為"/",那麼這個Servlet就是目前應用的預設Servlet,預設處理無法比對到虛拟路徑的請求。

​ 需要注意的是,路徑比對和擴充比對無法混合使用,即urlPattern無法寫成"/rest/*.do";這也是讓部分同學感到困惑的地方,Servlet的虛拟路徑比對并不是完全的按照正則來比對的,雖然路徑比對和擴充比對是按照正則中的通配符(*)來比對的,這也是部分同學可以會寫出特定的正則,但是卻不是一個合法的虛拟路徑;Servlet容器收到請求後,會将請求從上下文路徑(通過request.getContextPath()擷取的)處截斷,使用剩餘的部分來進行路徑比對,比如請求url為http://localhost:8080/FirstProject/HelloServlet,那麼Servlet容器就會使用"/HelloServlet"來比對Servlet。

​ 最後需要注意的是,我們說了上面四種比對規則,尤其是預設比對,可以比對到任意請求,那麼一個請求如果可以比對多個Servlet的虛拟路徑,那麼該執行哪個Servlet?其實啊,這些比對規則是有優先級的,具體的優先級為:精确比對>路徑比對>擴充名比對>預設比對,Servlet容器會從優先級高的虛拟路徑開始比對,比對到後就會立刻将請求交給對應的Servlet來處理,不會再關心其他Servlet的虛拟路徑是否會比對成功。

​ 下面我們來一組Servlet及其對應的虛拟路徑:

urlPatterns Servlet Name
/abc/* Servlet1
/ Servlet2
/abc Servlet3
*.do Servlet4

​ 當請求去除上下文路徑後路徑為:"/abc/a.html"時,根據上述規則,會調用Servlet1;

​ 請求為:"/abc",根據比對優先級,會調用Servlet3;

​ 請求為:"/abc/a.do",會比對到’/abc/*’、’*.do’,但根據比對優先級,會調用Servlet1;

​ 請求為:"/a.do",會比對到’/’、’*.do’,但根據比對優先級,會調用Servlet4;

3.Tomcat提供的預設Servlet

​ 為了測試預設Servlet,我們來進行一個測試。我們建立個SelfDefaultServlet,其urlPatterns我們配置為"/",其中的方法我們不做任何修改。

@WebServlet(
		description = "Self create default Servlet", 
		urlPatterns = { "/" }
		)
public class SelfDefaultServlet extends HttpServlet {
  //...
}
           

​ 我們啟動項目後,在浏覽器上輸入http://localhost:8080/FirstProject/hahaha或者其他任意無法比對到HelloServlet虛拟路徑的請求,發現頁面上的結果都如下所示,是不是這樣也不錯,不會報404錯誤了。

Servlet虛拟路徑映射詳解

​ 但是,此時我們想通路WebContent目錄下的靜态頁面(建立的一個welcome.html檔案),浏覽器上輸入http://localhost:8080/FirstProject/welcome.html,猜猜會發生什麼?我們來一起看下結果,如圖所示,請求結果并沒有按照我們的想法,根據請求路徑找到welcome.htm頁面,而是調用了SelfDefaultServlet,是不是很懵?

Servlet虛拟路徑映射詳解

​ 其實,用戶端的每個請求,都是由Servlet容器根據虛拟路徑的比對規則來進行處理的,包括靜态資源。并且,如果路徑輸入錯誤(去除了自己配置的預設Servlet後),我們常見的下面的錯誤,也是Servlet傳回給我們,哈哈,還是很意外?

Servlet虛拟路徑映射詳解

​ 我們能通過servlet友善簡單的開發網站,是因為我們站在了巨人的肩膀上,下面我們一起來看下Sun公司都為我們開發者提前做了些什麼工作。Tomcat會為項目配置一個預設的Servlet(如果項目中自行配置,則不會生效),配置檔案在tomcat安裝目錄下conf目錄中的web.xml檔案中,具體内容如下,預設的Servlet名為DefaultServlet。

<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處理的(自己單獨配置Servlet除外),如果請求檔案能找到,就會将頁面通過HttpServletResponse對象以流的方式傳回給用戶端,否則報404錯誤。

​ 不過講到這裡,大家可以自己試一試配置了預設Servelt時,通路welcome.html的情況(會調用SelfDefaultServlet),但是,如果我們在浏覽器中輸入http://localhost:8080/FirstProject/index.jsp(index.jsp是建立的第一個jsp頁面)呢?會是什麼樣一個結果?也是調用預設的Servlet麼?真是的運作結果如下:

Servlet虛拟路徑映射詳解

​ 這是什麼原因?為什麼不是調用預設的servlet了?這是因為tomcat除了預設Serlvet外,還給我們提供一個處理jsp檔案的Servlet,配置如下,因為字尾比對的優先級高于預設的Servlet,是以通路JSP的時候需要交由JspServlet來處理(JSP因為可能包含Java代碼,是以第一次執行的時候需要先編譯,這個工作由JspServlet完成)

<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>
  <url-pattern>*.jspx</url-pattern>
</servlet-mapping>
           

4.總結

​ 本文具體讨論了urlPatterns屬性的比對規則,主要為四種,其優先級也各不相同,我們在使用時,也需要根據自己的需求自己設定urlPatterns,不過知道了比對規則,使用起來也會友善很多,也能幫我們快速的定位錯誤。