SpringMVC若是在處理業務的過程當中發生了異常,這個時候是沒有一個完整的ModelAndView對象返回的,它應該是怎麼樣處理呢?或者說應該怎麼去獲取一個視圖而後去展現呢。下面就是要講的RequestToViewNameTranslator。html
DispathcerServlet在處理完請求獲取Me的lAndView以後就會獲取相應的視圖名稱,而後渲染解析。這個是經過調用doDispatch()中的processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);方法來完成的,可是若是在處理這個請求的過程當中發生了異常,而且這個異常類型不是ModelAndViewDefiningException類型的話,就會調用processHandlerException來處理一個異常,返回默認缺省的視圖:java
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) { exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } if (exMv != null) { if (exMv.isEmpty()) { return null; } // We might still need view name translation for a plain error model... if (!exMv.hasView()) { exMv.setViewName(getDefaultViewName(request)); } if (logger.isDebugEnabled()) { logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
在處理的過程當中有這麼一個須要注意exMv.setViewName(getDefaultViewName(request));若是沒有提供的視圖名字,那麼胸膛會根據請求來獲取一個默認的視圖名。來看下具體獲取默認視圖的過程。app
protected String getDefaultViewName(HttpServletRequest request) throws Exception { return this.viewNameTranslator.getViewName(request); }
原來是經過RequestToViewNameTranslator viewNameTranslator;的getViewName來獲取視圖名稱的。終於講到今天的主角RequestToViewNameTranslator接口及其的默認實現類DefaultRequestToViewNameTranslator。this
首先看源碼,url
public interface RequestToViewNameTranslator { String getViewName(HttpServletRequest request) throws Exception; }
這個接口只有一個方法,就是根據請求對象獲取一個視圖名稱。具體來說就是在沒有明確指定一個視圖名稱的時候,根據一個輸入的請求獲取一個邏輯視圖名稱。spa
做爲RequestToViewNameTranslator接口惟一的實現類,在沒有擴展指定接口時候的時候,系統就會加載這個做爲默認的實現。來看看具體時候涉及到的實現代碼,debug
public String getViewName(HttpServletRequest request) { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); return (this.prefix + transformPath(lookupPath) + this.suffix); } protected String transformPath(String lookupPath) { String path = lookupPath; if (this.stripLeadingSlash && path.startsWith(SLASH)) { path = path.substring(1); } if (this.stripTrailingSlash && path.endsWith(SLASH)) { path = path.substring(0, path.length() - 1); } if (this.stripExtension) { path = StringUtils.stripFilenameExtension(path); } if (!SLASH.equals(this.separator)) { path = StringUtils.replace(path, SLASH, this.separator); } return path; }
主要實現就是調用UrlPathHelper的getLookupPathForRequest的方法獲取一個looup路徑。transformPath方法主要是對獲取的路徑字符串再作個簡單處理罷了。主要是UrlPathHelper的getLookupPathForRequest的實現:rest
public String getLookupPathForRequest(HttpServletRequest request) { // Always use full path within current servlet context? if (this.alwaysUseFullPath) { return getPathWithinApplication(request); } // Else, use path within current servlet mapping if applicable String rest = getPathWithinServletMapping(request); if (!"".equals(rest)) { return rest; } else { return getPathWithinApplication(request); } } public String getPathWithinApplication(HttpServletRequest request) { String contextPath = getContextPath(request); String requestUri = getRequestUri(request); String path = getRemainingPath(requestUri, contextPath, true); if (path != null) { // Normal case: URI contains context path. return (StringUtils.hasText(path) ? path : "/"); } else { return requestUri; } } public String getPathWithinServletMapping(HttpServletRequest request) { String pathWithinApp = getPathWithinApplication(request); String servletPath = getServletPath(request); String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp); String path; // if the app container sanitized the servletPath, check against the sanitized version if(servletPath.indexOf(sanitizedPathWithinApp) != -1) { path = getRemainingPath(sanitizedPathWithinApp, servletPath, false); } else { path = getRemainingPath(pathWithinApp, servletPath, false); } if (path != null) { // Normal case: URI contains servlet path. return path; } else { // Special case: URI is different from servlet path. String pathInfo = request.getPathInfo(); if (pathInfo != null) { // Use path info if available. Indicates index page within a servlet mapping? // e.g. with index page: URI="/", servletPath="/index.html" return pathInfo; } if (!this.urlDecode) { // No path info... (not mapped by prefix, nor by extension, nor "/*") // For the default servlet mapping (i.e. "/"), urlDecode=false can // cause issues since getServletPath() returns a decoded path. // If decoding pathWithinApp yields a match just use pathWithinApp. path = getRemainingPath(decodeInternal(request, pathWithinApp), servletPath, false); if (path != null) { return pathWithinApp; } } // Otherwise, use the full servlet path. return servletPath; } } public String getPathWithinApplication(HttpServletRequest request) { String contextPath = getContextPath(request); String requestUri = getRequestUri(request); String path = getRemainingPath(requestUri, contextPath, true); if (path != null) { // Normal case: URI contains context path. return (StringUtils.hasText(path) ? path : "/"); } else { return requestUri; } }
首先判斷當前上下文的完整路徑是否爲空,若是不爲空就會調用getPathWithinApplication()方法返回這個路徑名字。若是爲空的話,則首先獲取到當前請求的映射的完整路徑,若是路徑不爲空就返回這個路徑,若是路徑爲空則返回當前請求的完整路徑了。code