技术标签: 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干活。
代码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继承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,对 redirect 和 forward 类型的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设置属性。
代码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处理。
Qt实现安卓沉浸式状态栏Qt实现安卓沉浸式状态栏Qt实现安卓沉浸式状态栏笔者写写此篇文章的时候,Qt已经发布了Qt 6版本,相比Qt 5版本有很大改变。所以此篇文章基于Qt 6.2而写。沉浸式状态栏的具体的实现过程,笔者参考了Qt android 设置系统状态栏为全透明,半透明、全屏,设置状态栏颜色,修改程序图标这篇文章,这篇里面写的很详细了,这篇文章的作者使用的是Qt 5,笔者这里主要写与Qt 6的不同,顺便简化一下过程。首先需要一个.java文件,使用Qt就可以新建一个.java文件,复制以下内容
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5792题目描述:给你n个值,每个值用A[i]表示,然后问你能否找到多少组(a,b,c,d)四个编号,四个编号互不相同,然后a < b, c < d,a代表的值小于b代表的值,c代表的值大于d代表的值。解题思路:先考虑a和b这两个编号,遍历每一个编号作为b,然后找到b前面有多少个小于b...
今天给大家分享采用AspNet MVC+前端框架LayUi实现文件上传功能,感兴趣的朋友可以学习一下。文件上传实体(UploadFile.cs) public class Upload...
参考文献:http://www.jianshu.com/p/a63595a41bedhttp://kaopubear.top/categories/ ####生物信息和编程RNA-seq基本流程下图是一个大概的RNA-seq基本流程把RNA破碎成小片段,然后将RNA转变成一条cD...
GetX是什么?关于GetX是什么,git上面有官方文档,GetX,这里有一篇详细介绍GetX使用详解,GetX使用详解无意间发现GetX由于flutter2的发布,感觉还是有必要去了解flutter了,最近也是打算系统学习flutter,之前也是有过打算,基本没付诸行动,当我在看状态管理provider相关知识时,无意间发现了GetX这一神器,所以就打算把之前写的首页底部导航,部分功能使用GetX改造一番。当我升级了flutter sdk到最新版后,在控制台敲入flutter run 直接跑在了浏
做为资深程序猿最大的快乐,就是苦中作乐。相信每个人在工作中总会碰到各种无语的场景,请大家在文章评论中留下最搞笑的瞬间,笑一笑十年少,笑多了,不怀孕。 1. 当客户要求兼容各大主流浏览器 2. 当我的代码终于编译成功时: 3. 当一个Bug出现在周五晚上时~~~ 4. 当组长问:你是否测试时: 5. 当你没测试代码就上传,结果完美运行时: 6. 凌...
出现 master -&gt; master (non-fast-forward)这个错误,表明:你的本地仓库是新的代码,而你的远程仓库还是之前版本的代码我在写项目的时候,下面这两种情况先出现了这种错误:(1)git init ——》 git remote add origin 项目地址 ——》 git pull origin master ——》 git add . ——》git c...
[TOC]## Brew工具### 简介brew又叫Homebrew,是Mac OSX上的软件包管理工具,能在Mac中方便的安装软件或者卸载软件, 命令操作, 非常方便。brew类似ubuntu系统下的apt-get的功能。### brew与brew cask关系与区别brew主要用来下载一些不带界面的命令行下的工具和第三方库。使用简单的指令,就能快速安装和升级本地的各种开发环境。brew cas...
读书笔记系列之:C语言中变量声明和定义 笛风 2013.11.24h
第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
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
git rm -r --cached . // 删除缓存git add -A // 重新添加文件git commit -m '更新.gitignore文件' //重新提交文件到仓库 ...