zoukankan      html  css  js  c++  java
  • Spring MVC工作原理及源码解析(四) ViewResolver实现原理及源码解析

    0、ViewResolver原理介绍

    根据视图的名称将其解析为 View 类型的视图,如通过 ModelAndView 中的视图名称将其解析成 View,View 是用来渲染页面的,也就是将 Model 填入模板中,生成 html 或其他格式的文件。
    可以设置多个解析策略,如可以根据 JSP 来解析,或者按照 Velocity 模版解析,如果设置了多个解析策略则可以通过 order 属性来设定其优先级,数值越小优先级越高,前面的视图解析器解析后就不会让后面的继续解析。默认的解析策略是 InternalResourceViewResolver,按照 JSP 页面来解析。ViewResolver 接口中的方法如下:
    • View resolveViewName(String viewName, Locale locale);

    该接口的实现类有AbstractCachingViewResolver、BeanNameViewResolver、ContentNegotiatingViewResolver、StandaloneMockMvcBuilder和ViewResolverComposite。

    1、AbstractCachingViewResolver:实现带缓存的ViewResolver

    来看上图中的第一个实现类:AbstractCachingViewResolver,该类实现了ViewResolver的resolveViewName接口,与其他实现类不同,AbstractCachingViewResolver实现的是带有缓存的ViewResolver。
    当前端控制器请求视图解析器解析 ModelAndView 时,AbstractCachingViewResolver实现的ViewResolver在解析时先从缓存里查找,如果找得到视图就返回,找不到就创建新的视图,具体代码如下所示:
    public View resolveViewName(String viewName, Locale locale) throws Exception {
            // 是否启用缓存,可通过setCache()方法或setCacheLimit()方法开启缓存,是一个ConcurrentHashMap,默认缓存大小1024
            if (!this.isCache()) {
                return this.createView(viewName, locale);
            } else {
                // 得到 view 在缓存中的 key 值
                Object cacheKey = this.getCacheKey(viewName, locale);
                View view = (View)this.viewAccessCache.get(cacheKey);
                // 如果没有找到 view 则创建,采用双重校验的方式进行安全创建
                if (view == null) {
                    synchronized(this.viewCreationCache) {
                        view = (View)this.viewCreationCache.get(cacheKey);
                        if (view == null) {
                            // 具体的创建方式由子类实现
                            view = this.createView(viewName, locale);
                            if (view == null && this.cacheUnresolved) {
                                view = UNRESOLVED_VIEW;
                            }
    
                            if (view != null) {
                                this.viewAccessCache.put(cacheKey, view);
                                this.viewCreationCache.put(cacheKey, view);
                                if (this.logger.isTraceEnabled()) {
                                    this.logger.trace("Cached view [" + cacheKey + "]");
                                }
                            }
                        }
                    }
                }
    
                return view != UNRESOLVED_VIEW ? view : null;
            }
        }    

     1.1、ResourceBundleViewResolver

    使用ResourceBundleViewResolver配置下bean就可以让视图解释器支持解析多种视图,而UrlBasedViewResolver,就只支持解释单一类型的视图。

    ResourceBundleViewResolver 根据 views.properties 文件来解析视图,这个文件位于 classpath 路径下,使用方式如下:
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">  
        <!-- 设定属性文件名为views -->  
        <property name="basename" value="views"></property>  
    </bean>  

    1.2、XmlViewResolver

    XmlViewResolver 根据 xml 文件来解析视图,使用方式如下:
    <bean class="org.springframework.web.servlet.view.XmlViewResolver">
        <property name="location">
            <value>/WEB-INF/spring-views.xml</value>
        </property>
    </bean>

    1.3、UrlBasedViewResolver

    支持解释单一类型的视图。

    UrlBasedViewResolver 提供了拼接 URL 的方式来解析视图,通过 prefix 属性拼接一个前缀,通过 suffix 属性拼接一个后缀,就得到了视图的 URL。还可以加入 redirect: 与 forword: 前缀,使用 redirect: 前缀会调用 HttpServletResponse对象的 sendRedirect() 方法进行重定向,使用 forword: 前缀会利用 RequestDispatcher的forword 方式跳转到指定的地址。另外,使用时还要指定 viewClass 属性,表示要解析成哪种 View,的使用方式如下:
    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">  
       <property name="prefix" value="/WEB-INF/" />  
       <property name="suffix" value=".jsp" />  
       <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>  
    </bean> 

    2、其他的 ViewResolver

    2.1、BeanNameViewResolver

    BeanNameViewResolver 是通过视图名称去容器中获取对应的 view 对象,所以在使用前需要将 view 对象注册到容器中。它没有使用缓存,实现方式如下:

        @Override
        public View resolveViewName(String viewName, Locale locale) throws BeansException {
            ApplicationContext context = getApplicationContext();
            // 根据viewName去容器中查找View对象
            if (!context.containsBean(viewName)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("No matching bean found for view name '" + viewName + "'");
                }
                // Allow for ViewResolver chaining...
                return null;
            }
            if (!context.isTypeMatch(viewName, View.class)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Found matching bean for view name '" + viewName +
                            "' - to be ignored since it does not implement View");
                }
                // Since we're looking into the general ApplicationContext here,
                // let's accept this as a non-match and allow for chaining as well...
                return null;
            }
            return context.getBean(viewName, View.class);
        }

    2.2、ContentNegotiatingViewResolver

    ContentNegotiatingViewResolver本身不解析解析视图,而是用来整合所有的ViewResolver类,每次请求都会遍历所有的ViewResolver,然后找到最合适的处理View,并将其返回。源码如下:

        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
            Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
            // 获取Request的MediaType集合
            List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
            if (requestedMediaTypes != null) {
                // 通过遍历ViewResolver,获取所有符合条件的View
                List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
                // 遍历所有的SmartView,SmartView默认是RedirectView返回
                // 否则,根据MediaType最合适的第一个View返回
                View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
                if (bestView != null) {
                    return bestView;
                }
            }
            if (this.useNotAcceptableStatusCode) {
                if (logger.isDebugEnabled()) {
                    logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
                }
                return NOT_ACCEPTABLE_VIEW;
            }
            else {
                logger.debug("No acceptable view found; returning null");
                return null;
            }
        }

    2.3、StandaloneMockMvcBuilder

    StandaloneMockMvcBuilder主要用于单元测试,代码如下所示:

        /**
         * A {@link ViewResolver} that always returns same View.(始终返回同一个View,用于单元测试)
         */
        private static class StaticViewResolver implements ViewResolver {
    
            private final View view;
    
            public StaticViewResolver(View view) {
                this.view = view;
            }
    
            @Override
            public View resolveViewName(String viewName, Locale locale) throws Exception {
                return this.view;
            }
        }

    2.4、ViewResolverComposite

    ViewResolverComposite是包含如上各个ViewResolver的组合类,其resolveViewName方法代码如下:

        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            for (ViewResolver viewResolver : this.viewResolvers) {
                // 生成View对象
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
            return null;
        }
    作者:blayn
    出处:https://www.cnblogs.com/blayn/
    版权:本文版权归作者和博客园共有
    转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
  • 相关阅读:
    高盛、沃尔玛 题做出来还挂了的吐槽
    amazon师兄debrief
    到所有人家距离之和最短的中点 296. Best Meeting Point
    问问题没人回答的情况怎么办终于有解了
    找名人 277. Find the Celebrity
    数组生存游戏 289. Game of Life
    547. Number of Provinces 省份数量
    428. Serialize and Deserialize Nary Tree 序列化、反序列化n叉树
    alias别名简介和使用
    面试官:线程池执行过程中遇到异常会发生什么,怎样处理? Vincent
  • 原文地址:https://www.cnblogs.com/blayn/p/14721860.html
Copyright © 2011-2022 走看看