• Spring之SpringMVC的RequestToViewNameTranslator(源码)分析


    前言

       SpringMVC如果在处理业务的过程中发生了异常,这个时候是没有一个完整的ModelAndView对象返回的,它应该是怎么样处理呢?或者说应该怎么去获取一个视图然后去展示呢。下面就是要讲的RequestToViewNameTranslator。

    1.引出问题

      DispathcerServlet在处理完请求获取Me的lAndView之后就会获取相应的视图名称,然后渲染解析。这个是通过调用doDispatch()中的processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);方法来完成的,但是如果在处理这个请求的过程中发生了异常,并且这个异常类型不是ModelAndViewDefiningException类型的话,就会调用processHandlerException来处理一个异常,返回默认缺省的视图:

    	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));如果没有提供的视图名字,那么胸膛会根据请求来获取一个默认的视图名。来看下具体获取默认视图的过程。

    protected String getDefaultViewName(HttpServletRequest request) throws Exception {
    		return this.viewNameTranslator.getViewName(request);
    	}
    

      原来是通过RequestToViewNameTranslator viewNameTranslator;的getViewName来获取视图名称的。终于讲到今天的主角RequestToViewNameTranslator接口及其的默认实现类DefaultRequestToViewNameTranslator。

     2.RequestToViewNameTranslator接口

    首先看源码,

    public interface RequestToViewNameTranslator {
    
    	String getViewName(HttpServletRequest request) throws Exception;
    
    }
    

      这个接口只有一个方法,就是根据请求对象获取一个视图名称。具体来讲就是在没有明确指定一个视图名称的时候,根据一个输入的请求获取一个逻辑视图名称。

    3.DefaultRequestToViewNameTranslator实现

    作为RequestToViewNameTranslator接口唯一的实现类,在没有扩展指定接口时候的时候,系统就会加载这个作为默认的实现。来看看具体时候涉及到的实现代码,

    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的实现:

    	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()方法返回这个路径名字。如果为空的话,则首先获取到当前请求的映射的完整路径,如果路径不为空就返回这个路径,如果路径为空则返回当前请求的完整路径了。

  • 相关阅读:
    堆栈详解
    结构体内存对齐
    const限定符
    硬盘及其分区(0819整理)
    Android编译环境搭建(0818-0819)
    wordpress导入模板数据
    git新建仓库
    android 镜像源
    js 获取浏览器可视窗口大小,滚动条高度
    jquery 获取浏览器可视窗口大小,滚动条高度
  • 原文地址:https://www.cnblogs.com/zhangminghui/p/4957901.html
Copyright © 2020-2023  润新知