深入SpringMVC视图解析器

    ViewResolver的主要职责是根据Controller所返回的ModelAndView中的逻辑视图名,为DispatcherServlet返回一个可用的View实例。SpringMVC中用于把View对象呈现给客户端的是View对象本身,而ViewResolver只是把逻辑视图名称解析为对象的View对象。因此,通常在接口层中,只需要返回一个view名称的字符串,ViewResolver就可以自动将字符串名称映射成对应的view进行输出。

    首先,我们先看看ViewResolver的顶层抽象接口:

1 public interface ViewResolver {
2 
3     /**
4      * Resolve the given view by name.
5      */
6     View resolveViewName(String viewName, Locale locale) throws Exception;
7 
8 }

其中,参数viewName表示待解析的视图名称,参数locale表示解析视图的区域,如果要实现国际化,就要注意该参数。

再看一下这个公共接口有哪些实现:

     (1)AbstractCachingViewResolver是一个抽象类,这种视图解析器会把它曾经解析过的视图保存起来,然后每次要解析视图的时候先从缓存里面找,如果找到了对应的视图就直接返回,如果没有就创建一个新的视图对象,然后把它放到一个用于缓存的map中,接着再把新建的视图返回。使用这种视图缓存的方式可以把解析视图的性能问题降到最低。可以查看下其源码:

 1 //默认的缓存大小
 2 public static final int DEFAULT_CACHE_LIMIT = 1024;
 3 
 4 //标志实际缓存大小
 5 private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
 6 
 7 //缓存页面存放的集合,可以不加锁的获取已经缓存的views,主要用于高并发访问。
 8 private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT);
 9 
10 //存放已经创建的views集合,主要用来统计淘汰策略
11 private final Map<Object, View> viewCreationCache =
12             new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
13                 @Override
14                 protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
15                     if (size() > getCacheLimit()) {
16                         viewAccessCache.remove(eldest.getKey());
17                         return true;
18                     }
19                     else {
20                         return false;
21                     }
22                 }
23             };
24 
25 
26 //实现接口的方法
27 @Override
28     public View resolveViewName(String viewName, Locale locale) throws Exception {
29              //没有开启缓存,创建views返回
30         if (!isCache()) {
31             return createView(viewName, locale);
32         }
33         else {//先从viewAccessCache中获取缓存
34             Object cacheKey = getCacheKey(viewName, locale);
35             View view = this.viewAccessCache.get(cacheKey);
36             //viewAccessCache中没有,去viewCreationCache中获取
37             if (view == null) { //
38                 synchronized (this.viewCreationCache) {
39                     view = this.viewCreationCache.get(cacheKey);
40                     if (view == null) {
41                         // 首先让子类去创建view
42                         view = createView(viewName, locale);
43                         if (view == null && this.cacheUnresolved) {
44                             view = UNRESOLVED_VIEW;
45                         }
46                         //缓存创建的view
47                         if (view != null) {
48                             this.viewAccessCache.put(cacheKey, view);
49                             this.viewCreationCache.put(cacheKey, view);
50                         
51                             }
52                         }
53                     }
54                 }
55             }
56                         //存在缓存,直接返回
57             return (view != UNRESOLVED_VIEW ? view : null);
58         }
59     }

至于这里为什么使用viewAccessCache和viewCreationCache两个缓存,viewAccessCache可以在不添加锁的情况下实现高并发获取已经缓存的views,后者存储的views和前者一样,方便用来制定缓存淘汰策略。

    (2)UrlBasedViewResolver是对ViewResolver的一种简单实现,而且继承了AbstractCachingViewResolver,主要就是提供的一种拼接URL的方式来解析视图,它可以让我们通过prefix属性指定一个指定的前缀,通过suffix属性指定一个指定的后缀,然后把返回的逻辑视图名称加上指定的前缀和后缀就是指定的视图URL。

 

posted @ 2019-04-25 17:07 jy的blog 阅读(...) 评论(...) 编辑 收藏