spring源码解析-web系列(六):九大组件之ViewResolver_binbin_civil的博客-程序员秘密

技术标签: spring  ♚java♚  spring源码解析  ViewResolver  

spring源码解析-web系列(一):启动
spring源码解析-web系列(二):处理请求的过程
spring源码解析-web系列(三):九大组件之HandlerMapping
spring源码解析-web系列(四):九大组件之HandlerAdapter
spring源码解析-web系列(五):解析请求参数
spring源码解析-web系列(六):九大组件之ViewResolver
spring源码解析-web系列(七):九大组件之HandlerExceptionResolver

转载请标明出处:
https://blog.csdn.net/bingospunky/article/details/98622667
本文出自马彬彬的博客

前言

ViewResolver的作用是通过ViewName获取到View,从而可以渲染结果。ViewResolver的接口定义如下:

代码1 (org.springframework.web.servlet.ViewResolver):

public interface ViewResolver {
    View resolveViewName(String var1, Locale var2) throws Exception;
}

ViewResolver的子类可以分为如下四类:
1.AbstractCachingViewResolver:提供了抽象缓存的能力,当缓存里不存在时,通过模板方法让子类创建。
2.BeanNameViewResolver:通过name去BeanFactory获取对象。简单至极,不予描述。
3.ContentNegotiatingViewResolver:内部分装了一些ViewResolver,具体工作通过内部封装的ViewResolver获取View,当获取到多个View时,该类通过MediaTypes(Content-type)选出一个最合适的View。
4.ViewResolverComposite:内部封装了一些ViewResolver,自己不干活,通过内部的ViewResolver干活。

AbstractCachingViewResolver

代码2 (org.springframework.web.servlet.view.AbstractCachingViewResolver.resolveViewName):

	public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!this.isCache()) {
            return this.createView(viewName, locale);
        } else {
            Object cacheKey = this.getCacheKey(viewName, locale);
            View view = (View)this.viewAccessCache.get(cacheKey);
            if (view == null) {
                Map var5 = this.viewCreationCache;
                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;
        }
    }

代码2主要就是对缓存的操作,如果缓存里有,直接从缓存里取;如果缓存里没有,那么通过代码2第12行的createView方法创建,createView又调用了模板方法loadView让子类创建。

这里使用了两个Map做缓存,viewAccessCache是ConcurrentHashMap类型的,主要用来做检索缓存,线程安全的且效率高;viewCreationCache是LinkedHashMap类型的,主要用来控制缓存数量限制,如果做线程安全,它的效率不如ConcurrentHashMap,所以存在了LinkedHashMap的基础上又使用了ConcurrentHashMap。

UrlBasedViewResolver

UrlBasedViewResolver继承AbstractCachingViewResolver,实现createView方法和loadView方法来创建具体的View。

代码3 (org.springframework.web.servlet.view.UrlBasedViewResolver.createView):

	protected View createView(String viewName, Locale locale) throws Exception {
        if (!this.canHandle(viewName, locale)) {
            return null;
        } else {
            String forwardUrl;
            if (viewName.startsWith("redirect:")) {
                forwardUrl = viewName.substring("redirect:".length());
                RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());
                return this.applyLifecycleMethods(viewName, view);
            } else if (viewName.startsWith("forward:")) {
                forwardUrl = viewName.substring("forward:".length());
                return new InternalResourceView(forwardUrl);
            } else {
                return super.createView(viewName, locale);
            }
        }
    }

代码3 UrlBasedViewResolver重写父类的createView,对 redirectforward 类型的ViewName进行了处理,创建了对应的View返回。否则还是调用父类的createView方法进行处理。

代码4 (org.springframework.web.servlet.view.UrlBasedViewResolver):

	protected View loadView(String viewName, Locale locale) throws Exception {
        AbstractUrlBasedView view = this.buildView(viewName);
        View result = this.applyLifecycleMethods(viewName, view);
        return view.checkResource(locale) ? result : null;
    }
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        AbstractUrlBasedView view = (AbstractUrlBasedView)BeanUtils.instantiateClass(this.getViewClass());
        view.setUrl(this.getPrefix() + viewName + this.getSuffix());
        String contentType = this.getContentType();
        if (contentType != null) {
            view.setContentType(contentType);
        }

        view.setRequestContextAttribute(this.getRequestContextAttribute());
        view.setAttributesMap(this.getAttributesMap());
        Boolean exposePathVariables = this.getExposePathVariables();
        if (exposePathVariables != null) {
            view.setExposePathVariables(exposePathVariables);
        }

        Boolean exposeContextBeansAsAttributes = this.getExposeContextBeansAsAttributes();
        if (exposeContextBeansAsAttributes != null) {
            view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
        }

        String[] exposedContextBeanNames = this.getExposedContextBeanNames();
        if (exposedContextBeanNames != null) {
            view.setExposedContextBeanNames(exposedContextBeanNames);
        }

        return view;
    }

代码4的loadView方法是父类的模板方法,用来创建具体和View。代码4第2行调用了this.buildView创建AbstractUrlBasedView。代码4第7行根据该ViewResolver里保存的ViewClass创建对象,代码4第8~第29行对这个对象设置一些参数。

至此,创建View的代码已经在UrlBasedViewResolver类中了,那么它的子类主要做的就是如下两件事情了:1.使用setViewClass设置对应的View类型(重写requiredViewClass对View’类型的限制)。2.给创建出来的View设置属性。

InternalResourceViewResolver

代码5 (org.springframework.web.servlet.view.InternalResourceViewResolver):

public class InternalResourceViewResolver extends UrlBasedViewResolver {
    private static final boolean jstlPresent = ClassUtils.isPresent("javax.servlet.jsp.jstl.core.Config", InternalResourceViewResolver.class.getClassLoader());
    private Boolean alwaysInclude;

    public InternalResourceViewResolver() {
        Class<?> viewClass = this.requiredViewClass();
        if (viewClass.equals(InternalResourceView.class) && jstlPresent) {
            viewClass = JstlView.class;
        }

        this.setViewClass(viewClass);
    }

    protected Class<?> requiredViewClass() {
        return InternalResourceView.class;
    }

    public void setAlwaysInclude(boolean alwaysInclude) {
        this.alwaysInclude = alwaysInclude;
    }

    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        InternalResourceView view = (InternalResourceView)super.buildView(viewName);
        if (this.alwaysInclude != null) {
            view.setAlwaysInclude(this.alwaysInclude);
        }

        view.setPreventDispatchLoop(true);
        return view;
    }
}

代码5第6第11行,在InternalResourceViewResolver的构造方法中,设置了ViewClass。代码5第23第29行代码中创建InternalResourceView对象,并且设置一些参数。

总结

ViewResolver的作用是通过ViewName获取到View,从而可以渲染结果。主要有AbstractCachingViewResolver、BeanNameViewResolver、ContentNegotiatingViewResolver、ViewResolverComposite,本文只描述了AbstractCachingViewResolver这个系列。

注意并不是每一个请求都会使用ViewResolver去获取View。我在 spring源码解析-web系列(四):九大组件之HandlerAdapter 这篇博客的ServletInvocableHandlerMethod这部分有提到使用returnValueHandler来处理返回值,并根据情况去渲染。如果是returnValueHandler没有渲染的,才会使用ViewResolver获取View来渲染;如果已经被returnValueHandler渲染过,那么不会再使用ViewResolver处理。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011647962/article/details/98622667

智能推荐

Qt实现安卓沉浸式状态栏_system_ui_flag_light_status_bar_如来说法的博客-程序员秘密

Qt实现安卓沉浸式状态栏Qt实现安卓沉浸式状态栏Qt实现安卓沉浸式状态栏笔者写写此篇文章的时候,Qt已经发布了Qt 6版本,相比Qt 5版本有很大改变。所以此篇文章基于Qt 6.2而写。沉浸式状态栏的具体的实现过程,笔者参考了Qt android 设置系统状态栏为全透明,半透明、全屏,设置状态栏颜色,修改程序图标这篇文章,这篇里面写的很详细了,这篇文章的作者使用的是Qt 5,笔者这里主要写与Qt 6的不同,顺便简化一下过程。首先需要一个.java文件,使用Qt就可以新建一个.java文件,复制以下内容

hdu5792 World is Exploding(多校第五场)树状数组求逆序对 离散化_weixin_34235135的博客-程序员秘密

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5792题目描述:给你n个值,每个值用A[i]表示,然后问你能否找到多少组(a,b,c,d)四个编号,四个编号互不相同,然后a &lt; b, c &lt; d,a代表的值小于b代表的值,c代表的值大于d代表的值。解题思路:先考虑a和b这两个编号,遍历每一个编号作为b,然后找到b前面有多少个小于b...

后端:Layui实现文件上传功能_IT技术分享社区的博客-程序员秘密

今天给大家分享采用AspNet MVC+前端框架LayUi实现文件上传功能,感兴趣的朋友可以学习一下。文件上传实体(UploadFile.cs) public class Upload...

37、链特异建库_weixin_30622181的博客-程序员秘密

参考文献:http://www.jianshu.com/p/a63595a41bedhttp://kaopubear.top/categories/ ####生物信息和编程RNA-seq基本流程下图是一个大概的RNA-seq基本流程把RNA破碎成小片段,然后将RNA转变成一条cD...

Flutter(进阶)GetX+BottomNavigationBar实现首页底部导航_辉涛的博客-程序员秘密

GetX是什么?关于GetX是什么,git上面有官方文档,GetX,这里有一篇详细介绍GetX使用详解,GetX使用详解无意间发现GetX由于flutter2的发布,感觉还是有必要去了解flutter了,最近也是打算系统学习flutter,之前也是有过打算,基本没付诸行动,当我在看状态管理provider相关知识时,无意间发现了GetX这一神器,所以就打算把之前写的首页底部导航,部分功能使用GetX改造一番。当我升级了flutter sdk到最新版后,在控制台敲入flutter run 直接跑在了浏

分享作为程序猿的快乐_weixin_30952103的博客-程序员秘密

做为资深程序猿最大的快乐,就是苦中作乐。相信每个人在工作中总会碰到各种无语的场景,请大家在文章评论中留下最搞笑的瞬间,笑一笑十年少,笑多了,不怀孕。 1. 当客户要求兼容各大主流浏览器 2. 当我的代码终于编译成功时: 3. 当一个Bug出现在周五晚上时~~~ 4. 当组长问:你是否测试时: 5. 当你没测试代码就上传,结果完美运行时: 6. 凌...

随便推点

! [rejected] master -> master (non-fast-forward)_叨唠的博客-程序员秘密

出现 master -&amp;gt; master (non-fast-forward)这个错误,表明:你的本地仓库是新的代码,而你的远程仓库还是之前版本的代码我在写项目的时候,下面这两种情况先出现了这种错误:(1)git init ——》 git remote add origin 项目地址 ——》 git pull origin master ——》 git add . ——》git c...

brew search怎么看版本_Homebrew使用文档_鸿鹄志US的博客-程序员秘密

[TOC]## Brew工具### 简介brew又叫Homebrew,是Mac OSX上的软件包管理工具,能在Mac中方便的安装软件或者卸载软件, 命令操作, 非常方便。brew类似ubuntu系统下的apt-get的功能。### brew与brew cask关系与区别brew主要用来下载一些不带界面的命令行下的工具和第三方库。使用简单的指令,就能快速安装和升级本地的各种开发环境。brew cas...

C语言中变量声明和定义----笛风读书笔记系列_c语言申明变量_笛风猪的博客-程序员秘密

读书笔记系列之:C语言中变量声明和定义                                                                              笛风                                                                              2013.11.24h

openstack pike 版搭建详解_时空无限的博客-程序员秘密

第1章 环境准备1.1 openstack各个版本安装包地址https://buildlogs.centos.org/centos/7/cloud/x86_64/至少两台centos 7或以上系统服务器1.2 主机名解释cat /etc/hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain loc

Android Studio怎么打包成APK_"jks 密钥库使用专用格式。建议使用 \"keytool -importkeystore -src_生活热爱就好的博客-程序员秘密

1.确定软件版本将项目切换到Project,打开app目录下的build.gradle文件versionCode是app的大版本好,为数值类型,默认为1,改成2versionName是app的具体版本号,为际符串类型,默认为1.0,改成2.02.指定生成的APK文件名还是在刚刚的bulid.gradle文件中修改,默认生成release版apk名为app-release.apk在android内部修改自己的apk名3.生成自己密钥签名的realease版apk直接点Build APK(s

修改了.gitignore文件不生效的办法_chenghao496723的博客-程序员秘密

git rm -r --cached . // 删除缓存git add -A // 重新添加文件git commit -m '更新.gitignore文件' //重新提交文件到仓库 ...

推荐文章

热门文章

相关标签