自滿是成功的殺手,唯有持續(xù)的奮斗才是成長的源泉
--蘇格拉底
最近在學(xué)習(xí)spring-mvc,在成功搭建spring-mvc的環(huán)境后,使用html寫了一個(gè)簡單的登陸頁面,但是在啟動(dòng)服務(wù)后,訪問相應(yīng)的頁面,報(bào)404,找不到對(duì)應(yīng)的資源。百思不得其解,于是乎跟了一下源碼,嘗試一下自己解決。
我使用的版本為:
- tomcat : 8.5.9
- spring-webmvc : 4.2.3.RELEASE
- jstl : 1.2
web.xml中DispatcherServlet的配置如下:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
跟源碼后發(fā)現(xiàn),在請(qǐng)求對(duì)應(yīng)的html頁面的時(shí)候,請(qǐng)求被DispatcherServlet處理了,在由其doDispatch處理的時(shí)候,無法獲取到對(duì)應(yīng)的handler。于是乎自作聰明,想著對(duì)對(duì)應(yīng)的html頁面寫一個(gè)Controller不就好了嗎?最后發(fā)現(xiàn)是不行的,暫時(shí)發(fā)現(xiàn)對(duì)應(yīng)Controller的頁面需要是jsp(這個(gè)可能是自己試用jstl解析視圖的原因,具體細(xì)節(jié)還要學(xué))。
可是之前自己在學(xué)習(xí)servlet的時(shí)候,是可以訪問靜態(tài)的html頁面的。為什么在使用spring-mvc的框架后會(huì)提示無法訪問呢?問題就出在DispatcherServlet的路徑映射上了。
servlet的路徑匹配規(guī)則
當(dāng)鍵入一個(gè)url后,servlet容器是如何進(jìn)行路徑的匹配的呢?具體的匹配規(guī)則是什么呢?匹配的順序是怎樣的?
首先,serlvet容器會(huì)用請(qǐng)求的url減去當(dāng)前應(yīng)用的上下文路徑,然后以剩余的字符串進(jìn)行匹配,假設(shè)現(xiàn)在我們請(qǐng)求的url為http://localhost:8080/servlet/index/index.html,他的應(yīng)用上下文為servlet,容器就會(huì)將http://localhost:8080/servlet去掉,使用/index/index.html進(jìn)行匹配,以選擇對(duì)應(yīng)處理請(qǐng)求的serlvet。
servlet 的匹配規(guī)則有四種,按匹配順序排列。
精確匹配。精確匹配的就是請(qǐng)求的路徑與servlet定義的url-pattern完全匹配。
路徑匹配。假如一個(gè)資源路徑下的所有資源頁面對(duì)應(yīng)的請(qǐng)求都需要有一個(gè)serlvet進(jìn)行處理,這可以使用路徑匹配規(guī)則。路徑匹配規(guī)則以
/開口,并以/*結(jié)尾
<!-- 對(duì)應(yīng)的servlet會(huì)處理以 /index/ 開頭的請(qǐng)求 -->
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>/index/*</url-pattern>
</servlet-mapping>
- 擴(kuò)展名匹配。匹配指定擴(kuò)展名字的文件,路徑匹配規(guī)則以
*.開頭
<!-- 對(duì)應(yīng)的servlet會(huì)處理對(duì)html文件的請(qǐng)求 -->
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
- 默認(rèn)匹配。默認(rèn)路徑匹配規(guī)則的路徑就一種:
/
??注意:在一個(gè)請(qǐng)求經(jīng)由servlet容器處理的時(shí)候,servlet容器按照順序進(jìn)行匹配,當(dāng)匹配中的時(shí)候,就將請(qǐng)求交給對(duì)應(yīng)的servlet進(jìn)行處理,單一匹配。
問題再次分析
很顯然,我將DispatcherServlet的匹配路徑定義為了默認(rèn)匹配,然后沒有定義其他的servlet,導(dǎo)致所有的請(qǐng)求都有DispatcherServlet進(jìn)行處理了。所有,如果定義一個(gè)可以處理靜態(tài)資源的serlvet就可以了,由此出發(fā),回想最初使用servlet的時(shí)候,為什么可以處理靜態(tài)資源的訪問呢?抱著疑問上網(wǎng)搜索了一下,發(fā)現(xiàn)原來在tomcat的conf目錄下由一個(gè)web.xml文件,打開這個(gè)web.xml文件,其中定義了DefaultServlet。我們看一下這個(gè)文件:

其對(duì)應(yīng)定義的匹配路徑為:

在我后續(xù)將DispatcherServlet的路徑映射定義為默認(rèn)后,我定義的覆蓋了tomcat的默認(rèn)的servlet,這就是問題原因了。找到問題原因了,怎么解決呢?
解決辦法
解決辦法一:
為tomcat的DefaultServlet定義一個(gè)優(yōu)先級(jí)更高的匹配。擴(kuò)展名匹配或者是路徑匹配。
<!-- 讓DefaultServlet來處理html,js,css文件 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<!-- 假設(shè)所有的資源都放在resource這個(gè)目錄下 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resource/*</url-pattern>
</servlet-mapping>
解決辦法二:
讓spring-webmvc來為我們解決靜態(tài)資源訪問的問題。spring中定義了一個(gè)DefaultServletHttpRequestHandler來處理靜態(tài)資源。我們可以看一下官方對(duì)它的介紹:

大概的意思就是,這個(gè)DefaultServletHttpRequestHandler是spring提供用來處理靜態(tài)資源的??梢栽趕pring的配置文件中對(duì)它進(jìn)行配置就好了。
<mvc:default-servlet-handler/>
<!-- 兩種選其中一種就好了 -->
<bean class="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler"></bean>
自此,靜態(tài)資源的訪問解決了,但是這其實(shí)解決了配置問題,其中具體的細(xì)節(jié)自己還是不了解,還是要深入進(jìn)行學(xué)習(xí)。學(xué)無止境??!