资讯专栏INFORMATION COLUMN

SpringMVC之源码分析--ViewResolver(五)

klinson / 2196人阅读

摘要:此解析器与差不多,更改下配置文件中的类全路径即可。总结本章介绍了以及三个视图解析器。这部分内容有点儿多,我会尽快结束。

概述

通过上几篇的学习,我们分析了并试验了ViewResolverComposite、BeanNameViewResolver和ContentNegotiatingViewResolver,这三个类都直接实现ViewResolver接口。Spring MVC提供了很多的ViewResolver实现,本章我们继续分析比较常用的几个视图解析器。

本系列文章是基于Spring5.0.5RELEASE。

AbstractCachingViewResolver

AbstractCachingViewResolver实现ViewResolver接口的抽象类,从类名可知,该类具有缓存功能,即缓存解析过的视图View对象,后续需要视图解析时,会先从缓存中查找,如果找到对应的视图就直接返回,如果未找到就创建一个视图对象放入缓存Map中,并返回创建对象。从其实现原理上来看,此类视图解析器的性能是最佳的。

public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver {
    /** 缓存视图map的初始大小 */
    public static final int DEFAULT_CACHE_LIMIT = 1024;
    
    /** 最大缓存数量 */
    private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;

    /** 解析过的View缓存容器,key是逻辑视图名,value是视图View对象 */
    private final Map viewAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT);

    /** 存储所有创建过的视图,本容器里的内容大于等于viewAccessCache中的内容
        key是逻辑视图名,value是视图View对象 */
    private final Map viewCreationCache =
        new LinkedHashMap(DEFAULT_CACHE_LIMIT, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                // 本容器内数量大于缓存数量时,移除最老的对象
                if (size() > getCacheLimit()) {
                    // 移除缓存容器中最老的View对象
                    viewAccessCache.remove(eldest.getKey());
                    return true;
                }
                else {
                    return false;
                }
            }
        };

    /** 实现ViewResolver接口resolveViewName方法 */
    @Override
    @Nullable
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        // 缓存开关(默认开启)。通过属性cacheLimit的值控制,大于0开启缓存,小于0关闭缓存
        if (!isCache()) {
            return createView(viewName, locale);
        }
        else {
            // 获取逻辑视图名称
            Object cacheKey = getCacheKey(viewName, locale);
            // 从缓存容器中查找缓存过的视图对象
            View view = this.viewAccessCache.get(cacheKey);
            // 缓存中未找到
            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;
                        }
                        if (view != null) {
                            // 放入已访问缓存容器
                            this.viewAccessCache.put(cacheKey, view);
                            // 放入已创建视图容器
                            this.viewCreationCache.put(cacheKey, view);
                            if (logger.isTraceEnabled()) {
                                logger.trace("Cached view [" + cacheKey + "]");
                            }
                        }
                    }
                }
            }
            // 返回View对象
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }
    ... ...
}

以上是AbstractCachingViewResolver的核心代码。简单说,本类就是实现了视图解析的缓存功能。

UrlBasedViewResolver

该类是ViewResolver接口的一种实现,并继承了AbstractCachingViewResolver抽象类,通过指定prefix前缀和suffix后缀,然后拼接逻辑视图名称加上前缀和后缀的方式确定视图URL。

UrlBasedViewResolver支持返回视图名称中包括redirect:前缀,以支持在客户端的跳转。比如当访问一个url"/demo",该url对应的handler返回的逻辑视图名为"redirect:/demo1",URLBasedViewResolver在创建视图时(createView方法中),判断逻辑视图名称的前缀是"redirect:"开头,接着裁剪掉"redirect:"前缀后,创建RedirectView对象,RedirectView对象将把请求返回的模型数据组合成查询参数形式拼接到redirect的URL后面,然后调用 HttpServletResponse 对象的 sendRedirect 方法进行重定向。(稍后我们实践验证)

同样的,URLBasedViewResolver还支持"forword:"前缀,然后封装成一个 InternalResourceView 对象,服务器端利用 RequestDispatcher 的 forword 方式跳转到指定的地址。

说了这么多,我们看下源码是如何实现的,如下:

public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {
    ... ...
    
    /** 创建View实例 */
    @Override
    protected View createView(String viewName, Locale locale) throws Exception {
        // If this resolver is not supposed to handle the given view,
        // return null to pass on to the next resolver in the chain.
        if (!canHandle(viewName, locale)) {
            return null;
        }
        // 处理redirect请求
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            // 去掉redirect:前缀
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            // 根据redirectUrl创建RedirectView实例
            RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
            String[] hosts = getRedirectHosts();
            if (hosts != null) {
                view.setHosts(hosts);
            }
            return applyLifecycleMethods(viewName, view);
        }
        // 处理forward请求
        if (viewName.startsWith(FORWARD_URL_PREFIX)) {
            去掉forward:前缀
            String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
            return new InternalResourceView(forwardUrl);
        }
        // Else fall back to superclass implementation: calling loadView.
        // 处理普通view(除redirect/forward)
        // 调用父类createView方法,然后通过模板方法再调回本例的loadView方法
        return super.createView(viewName, locale);
    }

    @Override
    protected View loadView(String viewName, Locale locale) throws Exception {
        AbstractUrlBasedView view = buildView(viewName);
        View result = applyLifecycleMethods(viewName, view);
        return (view.checkResource(locale) ? result : null);
    }

    ... ...
}
InternalResourceViewResolver

InternalResourceViewResolver继承UrlBasedViewResolver,故UrlBaseViewResolver具有的功能,InternalResourceViewResolver同样具备,在实际项目中也是使用最广泛的一种视图解析器。InternalResourceViewResolver会把返回的视图对象解析为InternalResourceView 对象,InternalResourceView 会把 Controller 处理器方法返回的模型属性都存放到对应的 request 属性中,然后通过 RequestDispatcher 在服务器端把请求 forword 重定向到目标 URL。代码如下:

public class InternalResourceViewResolver extends UrlBasedViewResolver {

    ... ...
    /** 构造函数 */
    public InternalResourceViewResolver() {
        // 获取InternalResourceView
        Class viewClass = requiredViewClass();
        if (InternalResourceView.class == viewClass && jstlPresent) {
            viewClass = JstlView.class;
        }
        setViewClass(viewClass);
    }

    @Override
    protected Class requiredViewClass() {
        return InternalResourceView.class;
    }

    ... ...

}

其他功能都与UrlBasedViewResolver一样。

实战练习

UrlBaseViewResolver

Spring配置文件代码如下:


    
    
    
    
    
    
    

Controller代码如下:

@Controller
public class DemoController {

    /** 测试redirect */
    @GetMapping("/redirect")
    public String redirect(ModelMap modelMap){
        modelMap.put("name","daliang");
        modelMap.put("pass","111");
        return "redirect:/demo";
    }
    /** 测试forward */
    @GetMapping("/forward")
    public String forward(ModelMap modelMap){
        modelMap.put("name","daliang");
        modelMap.put("pass","111");
        return "forward:/demo";
    }

    @GetMapping("/demo")
    public String demo(){
        return "test";
    }

}

启动应用,在浏览器地址栏输入http://localhost:8088/redirect,回车后如下:

可见参数拼接到了url后面。

InternalResourceViewResolver

此解析器与UrlBasedViewResolver差不多,更改下配置文件中的类全路径即可。

总结

本章介绍了AbstractCachingViewResolver、UrlBasedViewResolver以及InternalResourceViewResolver三个视图解析器。这部分内容有点儿多,我会尽快结束。希望能帮到大家,谢谢!

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69672.html

相关文章

  • SpringMVC源码分析--ViewResolver(三)

    摘要:概述本节学习下的功能,简单来说,该类的作用就是把多个视图解析器进行组装,内部使用存储配置使用的视图解析器。总结本章介绍了类,根据测试,了解到属性不影响中配置使用的视图解析器顺序。 概述 本节学习下ViewResolverComposite的功能,简单来说,该类的作用就是把多个ViewResolver视图解析器进行组装,内部使用list存储配置使用的视图解析器。 本系列文章是基于Spri...

    fox_soyoung 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(二)

    摘要:概述上篇学习了视图解析器作用及处理流程,为我们提供了丰富的视图解析器见下图本系列文章是基于。该视图解析器是根据处理器返回的逻辑视图名称,在应用上下文中查找该名称的视图对象视图对象就是的对象。 概述 上篇学习了Spring MVC ViewResolver视图解析器作用及处理流程,Spring为我们提供了丰富的视图解析器(见下图):showImg(https://segmentfault...

    jas0n 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(一)

    摘要:概述本章开始进入另一重要的组件,即视图组件,处理视图组件使用两个主要的接口是和。接口的作用是用于处理视图进行渲染。延用之前的介绍流程,本章分两部分进行阐述启动初始化和请求处理。 概述 本章开始进入另一重要的组件,即视图组件,Spring MVC处理视图组件使用两个主要的接口是ViewResolver和View。根据名称可知,ViewResolver即视图解析器,其作用是把逻辑视图名称解...

    pf_miles 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(四)

    摘要:概述本章继续学习另一个实现类解析器,该类的主要作用是根据同一请求的某些策略,选择对应的进行渲染。可以把理解为适配器,对不同类型进行适配。值得注意的是处理的为同一个。本系列文章是基于。实战需求目标实现后缀名或参数控制,显示不同的视图。 概述 本章继续学习ViewResolver另一个实现类ContentNegotiatingViewResolver解析器,该类的主要作用是根据同一请求的某...

    jcc 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(六)

    摘要:与一样,该类继承抽象类,并且通过外部的属性文件定义逻辑视图名称与真正的视图对象的关系,属性文件默认是下的,可以通过或属性来指定,该属性指的是文件的基名称,也就是说以属性值开头的属性文件。 概述 本章再学习另外两个ViewResolver,分别是XmlViewResolver和ResourceBundleViewResolver,从功能上说,这两个视图解析器都是从外部资源文件中查找视图V...

    alighters 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<