整个spring mvc的架构如下图所示:

上篇文件讲解了DispatcherServlet第二步:通过request从Controller获取ModelAndView。现在来讲解第三步:request 从ModelAndView中获取view对象。

获取view对象一般是通过viewResolver来解析view name来完成的。若ModelAndView中view 不存在或者ModelAndView本身为null则填充默认值。代码如下:

ModelAndView中view 不存在或者ModelAndView本身为null

DispatcherServlet doService--->doDispatcher-->applyDefaultViewName(request, mv);

    /*** Do we need view name translation?*/private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {if (mv != null && !mv.hasView()) {mv.setViewName(getDefaultViewName(request));}}

从request中解析出默认view name

    /*** Translate the supplied request into a default view name.* @param request current HTTP servlet request* @return the view name (or {@code null} if no default found)* @throws Exception if view name translation failed*/protected String getDefaultViewName(HttpServletRequest request) throws Exception {return this.viewNameTranslator.getViewName(request);}

我们先了解一下这个viewNameTranslator是怎么得到的吧?从容器中获取bean,bean 名称为:DefaultRequestToViewNameTranslator

    /*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}* Initialize the RequestToViewNameTranslator used by this servlet instance.* <p>If no implementation is configured then we default to DefaultRequestToViewNameTranslator.*/private void initRequestToViewNameTranslator(ApplicationContext context) {try {this.viewNameTranslator =context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);if (logger.isDebugEnabled()) {logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]");}}catch (NoSuchBeanDefinitionException ex) {// We need to use the default.this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);if (logger.isDebugEnabled()) {logger.debug("Unable to locate RequestToViewNameTranslator with name '" +REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + "': using default [" + this.viewNameTranslator +"]");}}}

获取默认view name的真正实现方法如下:

/*** Translates the request URI of the incoming {@link HttpServletRequest}* into the view name based on the configured parameters.* @see org.springframework.web.util.UrlPathHelper#getLookupPathForRequest* @see #transformPath*/@Overridepublic String getViewName(HttpServletRequest request) {String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);return (this.prefix + transformPath(lookupPath) + this.suffix);}/*** Transform the request URI (in the context of the webapp) stripping* slashes and extensions, and replacing the separator as required.* @param lookupPath the lookup path for the current request,* as determined by the UrlPathHelper* @return the transformed path, with slashes and extensions stripped* if desired*/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;}

完整实现如下:

/*** Return the mapping lookup path for the given request, within the current* servlet mapping if applicable, else within the web application.* <p>Detects include request URL if called within a RequestDispatcher include.* @param request current HTTP request* @return the lookup path* @see #getPathWithinApplication* @see #getPathWithinServletMapping*/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 applicableString rest = getPathWithinServletMapping(request);if (!"".equals(rest)) {return rest;}else {return getPathWithinApplication(request);}}/*** Return the path within the servlet mapping for the given request,* i.e. the part of the request's URL beyond the part that called the servlet,* or "" if the whole URL has been used to identify the servlet.* <p>Detects include request URL if called within a RequestDispatcher include.* <p>E.g.: servlet mapping = "/test/*"; request URI = "/test/a" -> "/a".* <p>E.g.: servlet mapping = "/test"; request URI = "/test" -> "".* <p>E.g.: servlet mapping = "/*.test"; request URI = "/a.test" -> "".* @param request current HTTP request* @return the path within the servlet mapping, or ""*/public String getPathWithinServletMapping(HttpServletRequest request) {String pathWithinApp = getPathWithinApplication(request);String servletPath = getServletPath(request);String 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;}}/*** Return the path within the web application for the given request.* <p>Detects include request URL if called within a RequestDispatcher include.* @param request current HTTP request* @return the path within the web application*/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;}}/*** Match the given "mapping" to the start of the "requestUri" and if there* is a match return the extra part. This method is needed because the* context path and the servlet path returned by the HttpServletRequest are* stripped of semicolon content unlike the requesUri.*/private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) {int index1 = 0;int index2 = 0;for (; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {char c1 = requestUri.charAt(index1);char c2 = mapping.charAt(index2);if (c1 == ';') {index1 = requestUri.indexOf('/', index1);if (index1 == -1) {return null;}c1 = requestUri.charAt(index1);}if (c1 == c2) {continue;}if (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2))) {continue;}return null;}if (index2 != mapping.length()) {return null;}if (index1 == requestUri.length()) {return "";}else if (requestUri.charAt(index1) == ';') {index1 = requestUri.indexOf('/', index1);}return (index1 != -1 ? requestUri.substring(index1) : "");}

ModelAndView中view 定义为view name,解析为view实例。

DispatcherServlet doService--->doDispatcher-->processDispatchResult--->render--->resolveViewName

    /*** Resolve the given view name into a View object (to be rendered).* <p>The default implementations asks all ViewResolvers of this dispatcher.* Can be overridden for custom resolution strategies, potentially based on* specific model attributes or request parameters.* @param viewName the name of the view to resolve* @param model the model to be passed to the view* @param locale the current locale* @param request current HTTP servlet request* @return the View object, or {@code null} if none found* @throws Exception if the view cannot be resolved* (typically in case of problems creating an actual View object)* @see ViewResolver#resolveViewName*/protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,HttpServletRequest request) throws Exception {for (ViewResolver viewResolver : this.viewResolvers) {View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}return null;}

InternalResourceViewResolver是具体实现:

    @Overrideprotected AbstractUrlBasedView buildView(String viewName) throws Exception {InternalResourceView view = (InternalResourceView) super.buildView(viewName);if (this.alwaysInclude != null) {view.setAlwaysInclude(this.alwaysInclude);}if (this.exposeContextBeansAsAttributes != null) {view.setExposeContextBeansAsAttributes(this.exposeContextBeansAsAttributes);}if (this.exposedContextBeanNames != null) {view.setExposedContextBeanNames(this.exposedContextBeanNames);}view.setPreventDispatchLoop(true);return view;}

/**
* Creates a new View instance of the specified view class and configures it.
* Does <i>not</i> perform any lookup for pre-defined View instances.
* <p>Spring lifecycle methods as defined by the bean container do not have to
* be called here; those will be applied by the {@code loadView} method
* after this method returns.
* <p>Subclasses will typically call {@code super.buildView(viewName)}
* first, before setting further properties themselves. {@code loadView}
* will then apply Spring lifecycle methods at the end of this process.
* @param viewName the name of the view to build
* @return the View instance
* @throws Exception if the view couldn't be resolved
* @see #loadView(String, java.util.Locale)
*/
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());

String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}

view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());

Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}

return view;
}

 

ModelAndView 本身存在view实例

            // No need to lookup: the ModelAndView object contains the actual View object.view = mv.getView();

不需要查询,使用getView方法即可获取到。

小结:

本文主要讲解request怎么从ModelAndView中获取view实例的,分三种情况:mv本身为空或者mv中view为空时,使用默认的view name;mv本身中view 为string 表示viewname而非view实例,那么根据view name使用viewResolver转换成view实例;mv本身有view实例则使用getview方法获取。

转载于:https://www.cnblogs.com/davidwang456/p/4132215.html

spring mvc DispatcherServlet详解之三---request通过ModelAndView中获取View实例的过程相关推荐

  1. spring mvc DispatcherServlet详解之一--request通过HandlerMaping获取控制器Controller过程

    整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的第一步:获取控制器. HandlerMapping HandlerMappi ...

  2. spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程

    整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet通过request获取控制器Controller的过程,现在来讲解DispatcherServletDisp ...

  3. spring mvc DispatcherServlet详解之前传---FrameworkServlet

    做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationCont ...

  4. spring mvc DispatcherServlet详解之四---视图渲染过程

    整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的最后一步:视图渲染.视图渲染的过程是在获取到ModelAndView后的过程 ...

  5. spring mvc DispatcherServlet详解之interceptor和filter的区别

    首先我们看一下spring mvc Interceptor的功能及实现: http://wenku.baidu.com/link?url=Mw3GaUhCRMhUFjU8iIDhObQpDcbmmRy ...

  6. spring mvc DispatcherServlet详解之一---处理请求深入解析

    要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构: 从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServl ...

  7. spring mvc DispatcherServlet详解之前传---前端控制器架构

    前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现 ...

  8. spring mvc DispatcherServlet详解前传---HttpServletBean类

    从上章里我们已经看到: DispatcherServlet extends FrameworkServlet FrameworkServlet extends HttpServletBean impl ...

  9. spring mvc DispatcherServlet详解之拾忆工具类utils

    DispatcherServlet的静态初始化 /*** Name of the class path resource (relative to the DispatcherServlet clas ...

最新文章

  1. GARFIELD@12-10-2004
  2. javaSE----eclipse的安装与使用
  3. mysql5.6 centos编译部署
  4. Flashback Query笔记
  5. python:else与循环语句联合用法
  6. Weird Game CodeForces - 299C
  7. 如何快速实现精准的个性化搜索服务
  8. Win7搭建NodeJs开发环境以及HelloWorld展示—图解
  9. java实现n选m组合数_求组合数m_n
  10. Hearing Range
  11. ConcurrentHashMap源码(JDK1.8)
  12. Qt的下载安装全教程
  13. windows虚拟显示器SDK开发和提供
  14. 空洞卷积(扩张卷积dilated convolution)
  15. 《智慧城市》顶层设计解读
  16. 2000坐标系和WGS84的关系
  17. lbochs模拟器最新版_Bochs模拟器下载_Bochs模拟器免费[系统增强]-下载之家
  18. 怎么判断二阶导数是否异号_二阶导数判断凹凸性 二阶导数怎么判断凹凸
  19. 测绘遥感地信 国内外期刊大全
  20. 物联网示范项目优秀案例集

热门文章

  1. mysql 驱动包 bin 的意思_mysql连接数据库,用下面代码,为什么找不到驱动呢?? lib下是mysql-connector-java-5.1.15-bin.jar...
  2. java sliplist_Java List retainAll()用法及代码示例
  3. 力扣第三题java_LeetCode 题解 | 力扣杯 LCP 06. 拿硬币
  4. Buck开关电源拓扑结构分析
  5. DualCircleList
  6. 登录功能和公聊功能的实现
  7. 对计算机技术的发展方向研究,网络技术发展对计算机技术的影响
  8. linux 内存 shared,Linux Shared Memory的查看与设置
  9. 计算机视觉编程——照相机模型
  10. 116. Leetcode 1143. 最长公共子序列 (动态规划-子序列问题)