Spring之SpringMVC的RequestToViewNameTranslator(源碼)分析

前言

   SpringMVC若是在處理業務的過程當中發生了異常,這個時候是沒有一個完整的ModelAndView對象返回的,它應該是怎麼樣處理呢?或者說應該怎麼去獲取一個視圖而後去展現呢。下面就是要講的RequestToViewNameTranslator。html

1.引出問題

  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

 2.RequestToViewNameTranslator接口

首先看源碼,url

public interface RequestToViewNameTranslator {

	String getViewName(HttpServletRequest request) throws Exception;

}

  這個接口只有一個方法,就是根據請求對象獲取一個視圖名稱。具體來說就是在沒有明確指定一個視圖名稱的時候,根據一個輸入的請求獲取一個邏輯視圖名稱。spa

3.DefaultRequestToViewNameTranslator實現

做爲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

相關文章
相關標籤/搜索