springmvc處理靜态資源,主要是兩個标簽,mvc:resources和mvc:default-servlet-handler。在詳細說明他們的原理之前,需要先簡單說明下springmvc中請求處理機制:handlermapping和handleradapter。
用過python django架構的都知道django對于通路方式的配置就是,一個url路徑和一個函數配對,你通路這個url,就會直接調用這個函數,簡單明了
然而對于springmvc架構來說,由于java的面向對象,就要找到對應的類以及對應的方法,是以就需要分成2步走
第一步 先找到url對應的處理類,叫handler,這裡就用到handlermapping來尋找
第二步 找到了對應的handler之後,我們該調用這個handler的哪個方法呢?這就需要handleradapter來決定
根據request請求,找到對應的handlerexecutionchain,handlerexecutionchain是handler和攔截器的結合。如下:
即針對某個請求,會有對應的handler和攔截器來處理。handlermapping僅僅是找到對應的handler和攔截器罷了,它并不限制handler的類型,任何一個存在于spring的ioc容器中的bean都可以成為handler,是以這個handler是object。
下面來看下常見的幾個handlermapping實作:
beannameurlhandlermapping : 對url直接配置一個bean作為這個url的handler。如在xml中如下配置
當我們通路 http://localhost:8080/index 時,就直接找到這個bean作為handler。
simpleurlhandlermapping : 上述隻能配置一個url對應的bean,simpleurlhandlermapping就可以配置多個,功能上更強大,它内部有一個map urlmap,存放着各個url對應的handler,如下
根據handlermapping找到了handler之後,我們該調用handler的哪個方法呢?handler又有哪些方法呢?這裡就需要采用擴充卡的模式,對不同的handler進行不同的處理。是以handleradapter的supports方法首先判斷這個handler是否是我能支援的,如果能支援,那我就按照我的處理模式來處理,即調用上述的handle方法。
下面來看下常見的幾個handleradapter的實作:
simpleservlethandleradapter : 它支援的handler必須是servlet,這樣的話該handler就必然有service(request, response)方法,是以就會調用handler的service(request, response)方法來處理請求,源碼如下
simplecontrollerhandleradapter : 它支援的handler必須是controller,controller接口定義了一個modelandview handlerequest(httpservletrequest request, httpservletresponse response)方法,是以我們知道該handler必然有一個handlerequest方法,就調用它來處理請求,源碼如下
httprequesthandleradapter : 它支援的handler必須是httprequesthandler,httprequesthandler接口定義了一個void handlerequest(httpservletrequest request, httpservletresponse response)方法,是以就知道該調用這個handler的handlerequest方法,源碼如下:
接下來就輪到重點了(上面的鋪墊夠長的了,哈哈)
來看下一般的mvc:resources的使用,如下:
然後來看源碼。首先要再次聲明下,所有在xml中配置的标簽,都會有對應的beandefinitionparser的實作類來進行處理,對于mvc:resources标簽,對應的實作類是resourcesbeandefinitionparser,檢視其中的源碼(這裡不再列出,自行去檢視),可以知道
注冊了一個simpleurlhandlermapping(上文已提到)。它是擁有一個map urlmap的,它把mvc:resources标簽中的mapping屬性作為key,把resourcehttprequesthandler作為handler。即/css/**類似的url請求,會由這個simpleurlhandlermapping比對到resourcehttprequesthandler上。
再看下,到底調用resourcehttprequesthandler的哪個方法來處理請求呢?
resourcehttprequesthandler實作了httprequesthandler,即是上文提到的httprequesthandleradapter支援的handler類型,是以就會調用resourcehttprequesthandler的void handlerequest(httpservletrequest request, httpservletresponse response)方法
其實很容易就明白了,resourcehttprequesthandler會根據mvc:resources标簽中的location屬性作為目錄,去尋找對應的資源,然後傳回資源的内容。這裡就不再詳細說明了,可以自行檢視resourcehttprequesthandler的所實作的handlerequest方法。
同理,mvc:default-servlet-handler标簽對應的beandefinitionparser的實作類是defaultservlethandlerbeandefinitionparser。
這裡注冊了simpleurlhandlermapping,它的map urlmap中存放了一個 key為/* ,對應的handler為defaultservlethttprequesthandler。即請求路徑比對 / * 的時候,這個simpleurlhandlermapping會交給defaultservlethttprequesthandler來處理。這種情況一般是其他handlermapping無法比對處理,最後才無奈交給defaultservlethttprequesthandler。
來看下defaultservlethttprequesthandler是怎麼處理的:
它同樣實作了httprequesthandler接口,擁有void handlerequest(httpservletrequest request, httpservletresponse response)方法,如下:
我們可以看到,這裡其實就是轉發給了web容器自身的servlet。這個servlet名稱可以在mvc:default-servlet-handler标簽中進行配置,如果沒有配置,采用預設的配置,如下:
其實這個時候,請求先經過tomcat的servlet的url-pattern的比對,進入到了springmvc,然後經過springmvc的handlermapping的一系列比對,沒有對應的handler比對,導緻又再次轉發給tomcat等預設的servlet上了,繞了很大的彎,是以要盡量避免這樣的操作。
這裡舉一個案例進行分析,在tomcat釋出的根目錄中,有一個a.html和a.jsp檔案,以及一個springmvc項目,如下:
其中springmvc項目配置了mvc:default-servlet-handler标簽,接下來以springmvc的dispatcherservlet的兩種配置進行說明,分别是
結果分别是:
dispatcherservlet配置為 / 的時候,a.html和a.jsp都可以正常通路到,如下
dispatcherservlet配置為 /* 的時候,a.html可以正常通路到,a.jsp就不行了,如下
分析如下:
通路a.html時:
當dispatcherservlet配置為 / 的時候,tomcat仍會選擇springmvc的dispatcherservlet來處理a.html-》它也處理不了,交給預設配置的mvc:default-servlet-handler來處理-》轉發到tomcat預設的servlet的,即defaultservlet來處理-》defaultservlet去尋找有沒有該檔案,找到了,傳回檔案内容
當dispatcherservlet配置為 /* 的時候,tomcat仍然是選擇springmvc的dispatcherservlet來處理a.html,同上面是一樣的過程
通路a.jsp時:
當dispatcherservlet配置為 / 的時候,tomcat會優先選擇自己已經預設注冊的jspservlet來處理-》jspservlet翻譯檔案内容,傳回
當dispatcherservlet配置為 /* 的時候,tomcat會選擇springmvc的dispatcherservlet來處理a.jsp-》發現springmvc找不到比對的handler,交給配置的mvc:default-servlet-handler來處理-》轉發到tomcat預設的servlet的,即defaultservlet來處理-》defaultservlet僅僅将a.jsp的源碼内容進行傳回