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

整个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
     */
    @Override
    public 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 applicable
        String 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是具体实现:

    @Override
    protected 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方法获取。

时间: 2024-10-25 06:05:57

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

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

整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet通过request获取控制器Controller的过程,现在来讲解DispatcherServletDispatcherServlet的第二步:通过request从Controller获取ModelAndView. DispatcherServlet调用Controller的过程: DispatcherServlet.java doService()--->doDispatch()--->handler

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

整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的最后一步:视图渲染.视图渲染的过程是在获取到ModelAndView后的过程. 视图渲染的过程: DispatcherServlet.java doService()--->doDispatch()--->processDispatchResult()--->render() processDispatchResult():主要处理异常.请求状态及触发请求完成事件,

spring mvc DispatcherServlet详解之---获取控制器

整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的第一步:获取控制器. HandlerMapping HandlerMappings 定义request和handler之间的映射.它的官方文档这样描述: Interface HandlerMapping All Known Implementing Classes: AbstractControllerUrlHandlerMapping, AbstractDetecting

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

首先我们看一下spring mvc Interceptor的功能及实现: http://wenku.baidu.com/link?url=Mw3GaUhCRMhUFjU8iIDhObQpDcbmmRy_IPeumazg0ppnbmwqFUtLp9kSpuPPpeysf6EnHBLYFeWrbjqMq8BlWKQz_7MSDhGQTVl32fpxCMm SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理,其他的作用比如通过它

spring mvc DispatcherServlet详解之一---前端控制器架构

前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架). DispatcherServlet 作为前置控制器是web服务器的入口,是spring mvc最重要的一个类,通过它的生命周期可以加深对web服务器的理解. servlet的生命周期 首先我们回忆一下ser

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

要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构: 从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServlet 就完全可以说弄清楚了spring mvc. 为了加深对spring mvc的整个工作流程的理解,本文从分析DispatcherServlet的工作过程来一窥spring mvc的整个面貌. 1. 初始化 protected void initStrategies(ApplicationCont

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

做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationContext. 首先我们来了解applicationContext 和WebapplicationContext区别和联系吧 1. ApplicationContext和WebApplicationContext是继承关系 /** * Interface to provide configuration

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

DispatcherServlet的静态初始化 /** * Name of the class path resource (relative to the DispatcherServlet class) * that defines DispatcherServlet's default strategy names. */ private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties&

转载:Spring MVC配置详解

以下内容引自:http://www.cnblogs.com/superjt/p/3309255.html spring MVC配置详解 现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理. 一.Spring MVC环境搭建:(Spring 2.5.6 + Hiber