在上一篇中我們初識了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錯誤了。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cGcq5iMzITM2YzY0MzYyEGMiVGOxYzXyITMyIDMzEzLcFTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL0M3Lc9CX6MHc0RHaiojIsJye.jpg)
但是,此時我們想通路WebContent目錄下的靜态頁面(建立的一個welcome.html檔案),浏覽器上輸入http://localhost:8080/FirstProject/welcome.html,猜猜會發生什麼?我們來一起看下結果,如圖所示,請求結果并沒有按照我們的想法,根據請求路徑找到welcome.htm頁面,而是調用了SelfDefaultServlet,是不是很懵?
其實,用戶端的每個請求,都是由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了?這是因為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,不過知道了比對規則,使用起來也會友善很多,也能幫我們快速的定位錯誤。