简单分析springmvc是如何解析view视图,并返回页面给前端

SpringMVC配置视图解析器

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property key="prefix" value="/WEB-INF/jsp/"/>
<property key="suffix" value=".jsp" />
</bean>

配置的为jsp的解析器

ViewResolver接口

其内部只有一个接口方法,具体如下

View resolveViewName(String viewName, Locale locale) throws Exception;

由上可知其是通过解析ViewName来得到View视图对象

ViewResolver实现类-BeanNameViewResolver

其意图是参数viewName就是springmvc上下文中的beanName对象,具体源码如下

	@Override
public View resolveViewName(String viewName, Locale locale) throws BeansException {
//获得springmvc上下文
ApplicationContext context = getApplicationContext();
if (!context.containsBean(viewName)) {
//viewName不存在,则直接返回null
return null;
}
if (!context.isTypeMatch(viewName, View.class)) {
//viewName对应的beanName不是View.class的实现类,则直接返回
return null;
}
return context.getBean(viewName, View.class);
}

BeanNameResolver表示返回的viewName必须是springmvc上下文中的beanName并且对应的实体类必须是View.class的实现类,否则返回null

ViewResolver实现类-AbstractCachingViewResolver

其是我们常用解析器的抽象类,比如Freemarker/Groovy,我们直接去看其实现的接口方法,具体源码如下

	@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
//不采用缓存方案,则每次都进行创建View
if (!isCache()) {
return createView(viewName, locale);
}
else {
//此处为使用缓存情况下的获取View对象
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) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
//默认会返回一个null的View对象
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}

接以上代码我们接着分析AbstractCachingViewResolver#createView()方法

	protected View createView(String viewName, Locale locale) throws Exception {
//此处的loadView便是模板方法,供子类去实现
return loadView(viewName, locale);
}

此处我们只分析其某个实现类UrlBasedViewResolver

UrlBasedViewResolver-对应请求url的解析器

  1. 常用的内部属性
	public static final String REDIRECT_URL_PREFIX = "redirect:";

	public static final String FORWARD_URL_PREFIX = "forward:";
//设置前缀
private String prefix = "";
//设置后缀
private String suffix = "";
//设置指定的viewName集合
private String[] viewNames ;
  1. createView()复写父类方法,即在创建view对象前做下跳转的请求检查
	@Override
protected View createView(String viewName, Locale locale) throws Exception {
//viewNames集合为null或者对应的viewName在viewNames集合内则返回true
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
//检查handler返回的值为string类型时是否包含"redirect:"前缀
//此处处理的便是跳转请求,比如"redirect:/user/list"
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
//同"redirect:"请求,此处为服务端直接跳转
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
//通过父类再去调用loadView()方法,其实是本类也复写了此方法
return super.createView(viewName, locale);
}
  1. loadView()复写方法
	@Override
protected View loadView(String viewName, Locale locale) throws Exception {
//创建View对象
AbstractUrlBasedView view = buildView(viewName);
//将view对象与viewName绑定注册至springmvc上下文中
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
  1. buildView()创建View对象主逻辑
	protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//获取类似FreemarkerView.class/GroovyView.class
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
//设置view对应的资源路径,此处便可知我们设置prefix和suffix的作用
view.setUrl(getPrefix() + viewName + getSuffix()); //下面都是设置与UrlBasedViewResolver的相关内部属性
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
} view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
//是否暴露路径变量
Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
//是否暴露springmvc的bean对象作为属性使用
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
} return view;
}

主要作用是设置prefix和suffix参数以及对应的内部属性,可自行查阅,并通过buildView()方法创建ViewClass属性指定的View对象

AbstractTemplateViewResolver-UrlBasedViewResolver子类

添加另外的属性

  1. 内部属性
	//是否暴露request对象的attributes属性给前端引擎
private boolean exposeRequestAttributes = false;
//是否允许请求处理过程中复写request对象的attributes
private boolean allowRequestOverride = false;
//是否暴露session对象的attributes属性给前端引擎
private boolean exposeSessionAttributes = false;
//是否允许请求处理过程中复写session对象的attributes
private boolean allowSessionOverride = false;
//是否使用暴露springMacroRequestContext属性
private boolean exposeSpringMacroHelpers = true;
  1. buildView()复写方法,主要是额外添加以上的属性
	@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//先调用父类的创建方法创建View对象
AbstractTemplateView view = (AbstractTemplateView) super.buildView(viewName);
//设置AbstractTemplateView的内部属性
view.setExposeRequestAttributes(this.exposeRequestAttributes);
view.setAllowRequestOverride(this.allowRequestOverride);
view.setExposeSessionAttributes(this.exposeSessionAttributes);
view.setAllowSessionOverride(this.allowSessionOverride);
view.setExposeSpringMacroHelpers(this.exposeSpringMacroHelpers);
return view;
}

在UrlBasedViewResolver的基础上设置额外的属性,属性集合见上文

FreeMarkerViewResolver-AbstractTemplateViewResolver子类

为方便理解,我们选取常用的模板引擎Freemarker,其他的引擎工具则供读者自行分析

  1. 构造函数-设置viewClass,满足上述的模板方法的viewClass的获取
	public FreeMarkerViewResolver() {
//设置的为FreemarkView.class
setViewClass(requiredViewClass());
}

小结

本节只解析了ViewResolver的简单逻辑,其根据配置的ViewClass属性,将配置的其他属性都设置到ViewClass对应的实例中,具体的关于视图的渲染,也就是view#render()方法我们放在下节讲解

SpringMVC源码情操陶冶-ViewResolver视图解析的更多相关文章

  1. SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器

    mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...

  2. SpringMVC源码情操陶冶-View视图渲染

    本节简单分析View视图对象的render方法 View接口 最重要的就是render()方法,具体源码如下 /** * Render the view given the specified mod ...

  3. SpringMVC源码情操陶冶-FreeMarker之web配置

    前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...

  4. SpringMVC源码情操陶冶-DispatcherServlet简析(二)

    承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...

  5. SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器

    承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ...

  6. SpringMVC源码情操陶冶-DispatcherServlet

    本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...

  7. SpringMVC源码情操陶冶-AbstractHandlerMethodMapping

    承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ...

  8. SpringMVC源码情操陶冶-DispatcherServlet类简析(一)

    阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...

  9. SpringMVC源码情操陶冶-HandlerAdapter适配器简析

    springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ...

随机推荐

  1. abstract关键字

    概念 abstract关键字表示的是抽象的意思,可以用来修饰类和方法.修饰类我们称为抽象类,修饰方法我们称为抽象方法. abstract修饰类,抽象类 1.public  abstract  clas ...

  2. 推荐xamlspy

    xamlspy(http://xamlspy.com/) 如果在win32时代用过spy++的,都应该在silverlight/wpf时代用一下xamlspy,让你重新找到用spy++看别人程序的UI ...

  3. javaCV开发详解之技术杂烩:javaCV能帮我们做什么?能实现什么功能?ffmpeg和openCV能实现功能,javaCV如何做到更快、更简单的实现相应的功能?等等一堆实用话题

    前言: 该篇文章旨在帮助刚接触javaCV的盆友系统的认识音视频.javaCV.图像处理相关的体系知识和一些实用的知识. 序: javaCV早期因为内置了openCV库,所以常用来做图像识别应用,现在 ...

  4. EF Core学习Code First

    下面通过实例来学习EF Core Code First,也就是通过EF Core迁移来完成从模型生成数据库. 本实例使用EntityFrameworkCore SQLite 数据库进行介绍,大家也可以 ...

  5. mac中使用 sourcetree 的快速配置和git服务器登录

    问题: 1.mac中下载sourcetree配置仓库地址,一直在提示输入密码,无法登录成功,更无法获取源码. 2.找不到配置仓库时的账号密码,只看到地址. 场景: git服务器:自己的GIT服务器,非 ...

  6. ZooKeeper源码分析-Jute-第二部分

    数据类型和流 本部分描述支持Hadoop的基础数据类型以及复合类型.我们的目的是支持一系列的类型,可以用于在不同的编程语言中简化和有效表达一定范围的记录类型. 基础类型 大部分情况下,Hadoop的大 ...

  7. github用法小结

      共享仓库     bare 裸仓库   生成裸仓库时必须以.git结尾.   仓库就相当于一个服务器     ### 创建远程仓库  1. 创建以.git结尾的目录mkdir repo.git 2 ...

  8. win7中python3.4下安装scrapy爬虫框架(亲测可用)

    貌似最新的scrapy已经支持python3,但是错误挺多的,以下为在win7中的安装步骤: 1.首先需要安装Scrapy的依赖包,包括parsel, w3lib, cryptography, pyO ...

  9. Swift组合逻辑

    我们可以组合多个逻辑运算来表达一个复合逻辑: if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverride ...

  10. Linux常用操作命令(一)

    java程序员要学习一些linux知识 java程序员要学习一些linux知识,下面就是您要学的命令:大型J2EE应用都在建构在linux环境下的.开发环境下我们可以通过samba映射成本地的网络驱动 ...