ViewResolver的主要职责是根据Controller所返回的ModelAndView中的逻辑视图名,为DispatcherServlet返回一个可用的View实例。SpringMVC中用于把View对象呈现给客户端的是View对象本身,而ViewResolver只是把逻辑视图名称解析为对象的View对象。因此,通常在接口层中,只需要返回一个view名称的字符串,ViewResolver就可以自动将字符串名称映射成对应的view进行输出。
首先,我们先看看ViewResolver的顶层抽象接口:
public interface ViewResolver { /**
* Resolve the given view by name.
*/
View resolveViewName(String viewName, Locale locale) throws Exception; }
其中,参数viewName表示待解析的视图名称,参数locale表示解析视图的区域,如果要实现国际化,就要注意该参数。
再看一下这个公共接口有哪些实现:
(1)AbstractCachingViewResolver是一个抽象类,这种视图解析器会把它曾经解析过的视图保存起来,然后每次要解析视图的时候先从缓存里面找,如果找到了对应的视图就直接返回,如果没有就创建一个新的视图对象,然后把它放到一个用于缓存的map中,接着再把新建的视图返回。使用这种视图缓存的方式可以把解析视图的性能问题降到最低。可以查看下其源码:
//默认的缓存大小
public static final int DEFAULT_CACHE_LIMIT = 1024; //标志实际缓存大小
private volatile int cacheLimit = DEFAULT_CACHE_LIMIT; //缓存页面存放的集合,可以不加锁的获取已经缓存的views,主要用于高并发访问。
private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT); //存放已经创建的views集合,主要用来统计淘汰策略
private final Map<Object, View> viewCreationCache =
new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
if (size() > getCacheLimit()) {
viewAccessCache.remove(eldest.getKey());
return true;
}
else {
return false;
}
}
}; //实现接口的方法
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
//没有开启缓存,创建views返回
if (!isCache()) {
return createView(viewName, locale);
}
else {//先从viewAccessCache中获取缓存
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
//viewAccessCache中没有,去viewCreationCache中获取
if (view == null) { //
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// 首先让子类去创建view
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
//缓存创建的view
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view); }
}
}
}
}
//存在缓存,直接返回
return (view != UNRESOLVED_VIEW ? view : null);
}
}
至于这里为什么使用viewAccessCache和viewCreationCache两个缓存,viewAccessCache可以在不添加锁的情况下实现高并发获取已经缓存的views,后者存储的views和前者一样,方便用来制定缓存淘汰策略。
(2)UrlBasedViewResolver是对ViewResolver的一种简单实现,而且继承了AbstractCachingViewResolver,主要就是提供的一种拼接URL的方式来解析视图,它可以让我们通过prefix属性指定一个指定的前缀,通过suffix属性指定一个指定的后缀,然后把返回的逻辑视图名称加上指定的前缀和后缀就是指定的视图URL。