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;
}

Spring MVC工作原理及源码解析(四) ViewResolver实现原理及源码解析的更多相关文章

  1. Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析

    1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...

  2. Spring MVC工作原理及源码解析(一) MVC原理介绍、与IOC容器整合原理

    MVC原理介绍 Spring MVC原理图 上图是Spring MVC工作原理图(图片来自网上搜索),根据上图,我们可以得知Spring MVC的工作流程如下: 1.用户(客户端,即浏览器)发送请求至 ...

  3. Spring MVC工作原理(好用版)

    Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...

  4. Zookeeper 源码(四)Zookeeper 服务端源码

    Zookeeper 源码(四)Zookeeper 服务端源码 Zookeeper 服务端的启动入口为 QuorumPeerMain public static void main(String[] a ...

  5. Spring MVC工作原理及源码解析(二)DispatcherServlet实现原理及源码解析

    1.DispatcherServlet 处理流程 从上一篇文章中Spring MVC原理图中我们可以看出:DispatcherServlet 在 Spring MVC框架 中处于核心位置,它负责协调和 ...

  6. Spring MVC工作原理 及注解说明

    SpringMVC框架介绍 1) spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web 应用程序的全功 ...

  7. spring mvc 工作原理

    SpringMVC的工作原理图: SpringMVC流程 1.  用户发送请求至前端控制器DispatcherServlet. 2.  DispatcherServlet收到请求调用HandlerMa ...

  8. Spring MVC 工作原理和流程、注解

    Spring MVC 是实现MVC设计模式的企业级开发框架,是Spring框架的一个子模块,无需整合,开发起来更加便捷. MVC设计模式 MVC是一种设计模式,它将应用程序分为 Controller. ...

  9. 浅析Spring MVC工作机制

    1.如何使用Spring MVC? 在web.xml中配置一个DispatcherServlet DispatchServlet初始化的时候会去寻找一个在应用程序的WEB-INF目录下的配置文件,命名 ...

随机推荐

  1. Python深入:setuptools进阶

    作者:gqtcgq 来源:CSDN 原文:https://blog.csdn.net/gqtcgq/article/details/49519685 Setuptools是Python Distuti ...

  2. 让你的浏览器变成Siri一样的语音助手

    最近业余时间浏览技术文章的时候,看到了一篇关于语音朗读的文章:Use JavaScript to Make Your Browser Speak(用Javascript让你的浏览器说话),文章中提到可 ...

  3. 【死磕JVM】一道面试题引发的“栈帧”!!!

    前言 最近小农的朋友--小勇在找工作,开年来金三银四,都想跳一跳,找个踏(gao)实(xin)点的工作,这不小勇也去面试了,不得不说,现在面试,各种底层各种原理,层出不穷,小勇就遇上了这么一道面试题, ...

  4. C# 通过ServiceStack 操作Redis——ZSet类型的使用及示例

    Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列 /// <summary> /// Sorted Sets是将 ...

  5. Paint Chain HDU - 3980

    题目链接:https://vjudge.net/problem/HDU-3980 题意:由n个石头组成的环,每次只能取连续的M个,最后不能取得人输. 思路:这样就可以先把它变成链,然后在链上枚举取m个 ...

  6. 小白的第一次sql实战

    去年发的有一篇sql注入忘记粘贴过来了,今天想起了就fuzz过来一下 有id尝试sql注入 找这种sql注入的站用sql检索就行了,但是最好挂代理用谷歌搜索,百度的话搜sql注入的很多被别人打过了,导 ...

  7. Python基础之:Python中的类

    目录 简介 作用域和命名空间 class 类对象 类的实例 实例对象的属性 方法对象 类变量和实例变量 继承 私有变量 迭代器 生成器 简介 class是面向对象编程的一个非常重要的概念,python ...

  8. Codeforces-121C(逆康托展开)

    题目大意: 给你两个数n,k求n的全排列的第k小,有多少满足如下条件的数: 首先定义一个幸运数字:只由4和7构成 对于排列p[i]满足i和p[i]都是幸运数字 思路: 对于n,k<=1e9 一眼 ...

  9. MySQL-一条sql语句的执行顺序

    手写: SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOIN <right_tab ...

  10. 中小型前端团队代码规范工程化最佳实践 - ESLint

    前言 There are a thousand Hamlets in a thousand people's eyes. 一千个程序员,就有一千种代码风格.在前端开发中,有几个至今还在争论的代码风格差 ...