Spring MVC使用過程當中遇到了這樣一個問題,web.xml有以下的配置,注意兩個url-pattern是兩種不一樣的url模式,*.html主要是處理通常的頁面請求,而/file/view/*主要是用於文件下載,由於經過response設置下載文件名和文件contentType太過麻煩,因此用了個取巧的方法把文件名做爲url的一部分,因爲文件後綴有不少種,就無法單靠*.html攔截。html
<!-- Spring MVC前端處理器 --> <servlet> <servlet-name>Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <description>Spring MVC定義Bean文件,該文件爲空配置,全部配置交給上級WebApplicationContext來處理</description> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/servlet-empty-context.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Dispatcher Servlet</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Dispatcher Servlet</servlet-name> <url-pattern>/file/view/*</url-pattern> </servlet-mapping>
而後就有以下的控制器(不相干的代碼就跳過了):前端
@Controller @RequestMapping("/file/*") public class FileAction extends AbstractAction { @RequestMapping(value = "upload.html", method=RequestMethod.POST) public String upload() { // ... } @RequestMapping(value = "view/{id}/*", method=RequestMethod.GET) public void view(@PathVariable("id") int id) { // ... } }
結果實際使用時,用做文件上傳upload方法跟請求很容易就匹配到了,可是文件下載哪怕是.html結尾的請求也一直就沒法匹配致使404.而去掉web.xml中的/file/view/*就正常了。java
Debug進Spring源碼,發現了類org.springframework.web.util.UrlPathHelper,他被AbstractHandlerMapping做爲屬性,並調用它的getLookupPathForRequest()方法以請求對象返回一個用於查找匹配項的lookupPath。而這個方法的返回值受其屬性alwaysUseFullPath的影響。默認alwaysUseFullPath爲false,這時候UrlPathHelper會在原url的基礎上截去request.getServletPath()的部分,也就是 /file/view,因此獲得的lookupPath就沒法匹配了。web
如今問題的關鍵就變成了修改AbstractHandlerMapping中UrlPathHelper的alwaysUseFullPath爲true。因爲UrlPathHelper是AbstractHandlerMapping直接new出來的,沒有在Spring的容器中,沒法直接獲取,可是發現經過調用AbstractHandlerMapping的setAlwaysUseFullPath()方法能夠達到一樣的目的(內部本身會改UrlPathHelper的alwaysUseFullPath)。spring
因爲我是直接使用mvc的namespace配置的annotation-driven,找了半天實在找不到如何在配置上對自動生成的HandlerMapping屬性進行修改的辦法,不過想到UrlPathHelper的getLookupPathForRequest()是在每一個請求到達時才調用,在容器初始化到請求到達之間修改它既可,因此單獨定義了一個SetAlwaysUseFullPathModule模塊來作這個事情。mvc
public class SetAlwaysUseFullPathModule { @Resource public void setHandlerMapping(RequestMappingHandlerMapping handlerMapping) { handlerMapping.setAlwaysUseFullPath(true); } }
把這個類部署到Spring配置文件中,再測試下載功能就正常了。app
注:這裏注入的RequestMappingHandlerMapping是Spring3.1加入的,3.2時它代替本來的DefaultAnnotationHandlerMapping做爲Spring MVC annotation-driven的默認HandlerMapping,具體使用哪個應視狀況而定。測試