目录

前言

SpringMVC是目前主流的Web MVC框架之一。

如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html

本文将分析SpringMVC的视图这部分内容,让读者了解SpringMVC视图的设计原理。

重要接口和类介绍

1. View接口

  视图基础接口,它的各种实现类是无状态的,因此是线程安全的。 该接口定义了两个方法: 

2. AbstractView抽象类

  View接口的基础实现类。我们稍微介绍一下这个抽象类。

  首先看下这个类的属性:

  再看下抽象类中接口方法的实现:

  getContentType方法直接返回contentType属性即可。

  render方法:

3. AbstractUrlBasedView抽象类

  继承自AbstractView抽象类,增加了1个类型为String的url参数。

4. InternalResourceView类

  继承自AbstractUrlBasedView抽象类的类,表示JSP视图。

  我们看下这个类的renderMergedOutputModel方法(AbstractView抽象类定义的抽象方法,为View接口提供的render方法服务)。

5. JstlView类

  JSTL视图,继承自InternalResourceView,该类大致上与InternalResourceView类一致。

6. AbstractTemplateView抽象类

  继承自AbstractUrlBasedView抽象类,重写了renderMergedOutputModel方法,在该方法中会调用renderMergedTemplateModel方法,renderMergedTemplateModel方法为新定义的抽象方法。

  该抽象类有几个boolean属性exposeSessionAttributes,exposeRequestAttributes。 设置为true的话会将request和session中的键值和值丢入到renderMergedTemplateModel方法中的model这个Map参数中。

  这个类是某些模板引擎视图类的父类。 比如FreemarkerView,VelocityView。

7. FreeMarkerView类

  继承自AbstractTemplateView抽象类。

  直接看renderMergedTemplateModel方法,renderMergedTemplateModel内部会调用doRender方法:

8. RedirectView类

  继承自AbstractUrlBasedView,并实现SmartView接口。SmartView接口定义了1个boolean isRedirectView();方法。

  该视图的renderMergedOutputModel方法主要就是通过response.sendRedirect进行重定向。

  有关RedirectView方面的知识楼主另外写了1篇博客。http://www.cnblogs.com/fangjian0423/p/springMVC-redirectView-analysis.html

9. ViewResolver接口

  视图解释器,用来解析视图View,与View接口配合使用。

  该接口只有1个方法,通过视图名称viewName和Locale对象得到View接口实现类:

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

10. AbstractCachingViewResolver抽象类

  带有缓存功能的ViewResolver接口基础实现抽象类,该类有个属性名为viewAccessCache的以 "viewName_locale" 为key, View接口为value的Map。

  该抽象类实现的resolveViewName方法内部会调用createView方法,方法内部会调用loadView抽象方法。

11. UrlBasedViewResolver类

  继承自AbstractCachingViewResolver抽象类、并实现Ordered接口的类,是ViewResolver接口简单的实现类。

  该类复写了createView方法:

  父类(AbstractCachingViewResolver)的createView方法内部会调用loadView抽象方法,UrlBasedViewResolver实现了这个抽象方法:

  下面对UrlBasedViewResolver做1个test,配置如下:

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
<property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>
<property name="viewNames">
<array>
<value type="java.lang.String">*</value>
</array>
</property>
<property name="contentType" value="text/html;charset=utf-8"/>
<property name="attributesMap">
<map>
<entry key="mytest" value="mytestvalue"/>
</map>
</property>
<property name="attributes">
<props>
<prop key="test">testvalue</prop>
</props>
</property>
</bean>

  我们看到:以InternalResourceView这个JSP视图作为视图;viewNames我们设置了*,这里的*代表全部视图名(这个viewNames属性不设置也可以,代表全部视图名都处理);http响应头部contentType信息:text/html;charset=utf-8;attributesMap和attributes传入的Map和Properties参数都会被丢入到staticAttributes属性中,这个staticAttributes会被设置成AbstractView的staticAttributes属性,也就是request域中的参数。

  我们看到request域中没有设置mytest和testvalue值。但是页面中会显示,因为我们配置了attributesMap和attributes参数。

  如果我们把viewNames中的"*"改成"index1"。那么就报错了,因为处理视图名的时候index匹配不上index1。

12. InternalResourceViewResolver类

  继承自UrlBasedViewResolver,以InternalResourceView作为视图,若项目中存在“javax.servlet.jsp.jstl.core.Config”该类,那么会以JstlView作为视图。重写了buildView方法,主要就是为了给InternalResourceView视图设置属性。

13. AbstractTemplateViewResolver类

  继承自UrlBasedViewResolver,重写了buildView方法,主要就是构造AbstractTemplateView以及为它设置相应的属性。

14. FreeMarkerViewResolver类

  继承自AbstractTemplateViewResolver,将视图设置为FreeMarkerView。

15. ModelAndView对象

  顾名思义,带有视图和Model属性的一个模型和视图类。

  值得注意的是,这个视图属性是一个Object类型的数据,可以直接是View接口的实现类或者视图名(字符串)。

源码分析

下面我们来分析SpringMVC处理视图的源码。

SpringMVC在处理请求的时候,通过RequestMappingHandlerMapping得到HandlerExecutionChain,然后通过RequestMappingHandlerAdapter得到1个ModelAndView对象,之后通过processDispatchResult方法处理。

processDispatchResult方法如下:

如果配置的ViewResolver如下:

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

那么就是使用InternalResourceViewResolver来解析视图。

之前分析过,InternalResourceViewResolver重写了UrlBasedViewResolver的buildView方法。但是还是会调用UrlBasedViewResolver的buildView方法。

最终得到InternalResourceView或JstlView视图。这两个视图的render方法本文介绍重要接口及类的时候已分析。

PS:DispathcerServlet中的viewResolvers属性是个集合,如果存在多个ViewResolver对象,必定会有优先级的问题,这部分的内容请参考楼主的另一篇博客:

http://www.cnblogs.com/fangjian0423/p/spring-Ordered-interface.html

编码自定义的ViewResolver

下面,我们就来编写自定义的ViewResolver。

自定义的ViewResolver处理视图名的时候,遇到 "jsp:" 开头的会找jsp页面,遇到 "freemarker:" 开头的找freemarker页面。

public class CustomViewResolver extends UrlBasedViewResolver {

  public static final String JSP_URL_PREFIX = "jsp:";
public static final String FTL_URL_PREFIX = "freemarker:"; private static final boolean jstlPresent = ClassUtils.isPresent(
"javax.servlet.jsp.jstl.core.Config", CustomViewResolver.class.getClassLoader()); private Boolean exposePathVariables = false; private boolean exposeRequestAttributes = false; private boolean allowRequestOverride = false; private boolean exposeSessionAttributes = false; private boolean allowSessionOverride = false; private boolean exposeSpringMacroHelpers = true; public CustomViewResolver() {
this.setViewClass(FreeMarkerView.class);
} @Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
if(viewName.startsWith(FTL_URL_PREFIX)) {
return buildFreemarkerView(viewName.substring(FTL_URL_PREFIX.length()));
} else if(viewName.startsWith(JSP_URL_PREFIX)) {
Class viewCls = jstlPresent ? JstlView.class : InternalResourceView.class;
return buildView(viewCls, viewName.substring(JSP_URL_PREFIX.length()), getPrefix(), ".jsp");
} else {
//默认以freemarker处理
return buildFreemarkerView(viewName);
}
} private AbstractUrlBasedView build(Class viewClass, String viewName, String prefix, String suffix) {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
view.setUrl(prefix + viewName + suffix);
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
if (this.exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
return view;
} private AbstractUrlBasedView buildFreemarkerView(String viewName) throws Exception {
AbstractTemplateView view = (AbstractTemplateView) build(FreeMarkerView.class, viewName, "", getSuffix());
view.setExposeRequestAttributes(this.exposeRequestAttributes);
view.setAllowRequestOverride(this.allowRequestOverride);
view.setExposeSessionAttributes(this.exposeSessionAttributes);
view.setAllowSessionOverride(this.allowSessionOverride);
view.setExposeSpringMacroHelpers(this.exposeSpringMacroHelpers);
return view;
} //get set方法省略 }

xml配置:

<bean class="org.format.demo.support.viewResolver.CustomViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".ftl"/>
<property name="contentType" value="text/html;charset=utf-8"/>
<property name="exposeRequestAttributes" value="true"/>
<property name="exposeSessionAttributes" value="true"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="requestContextAttribute" value="request"/>
</bean>
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/view/"/>
<property name="defaultEncoding" value="utf-8"/>
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">10</prop>
<prop key="locale">zh_CN</prop>
<prop key="datetime_format">yyyy-MM-dd</prop>
<prop key="date_format">yyyy-MM-dd</prop>
<prop key="number_format">#.##</prop>
</props>
</property>
</bean>

简单解释一下:CustomViewResolver解析视图名的时候,判断 "jsp:" 和 "freemarker:" 开头的名字,如果是 "jsp:" 开头的,如果有JSTL依赖,构造JSTLView视图,否则构造InternalResourceView视图。如果是 "freemarker:" 构造FreemarkerView。在构造视图之前分别会设置一些属性。

xml配置:配置prefix是为了给jsp视图用的,freemarker视图不需要prefix,因为FreemarkerView内部会使用配置的FreeMarkerConfigurer,并用FreeMarkerConfigurer内部的templateLoaderPath属性作为前缀,配置的suffix是为了让FreemarkerView使用,当后缀。

最后附上Controller代码:

@Controller
@RequestMapping(value = "/tvrc")
public class TestViewResolverController { @RequestMapping("jsp")
public ModelAndView jsp(ModelAndView view) {
view.setViewName("jsp:trvc/index");
return view;
} @RequestMapping("/ftl")
public ModelAndView freemarker(ModelAndView view) {
view.setViewName("freemarker:trvc/index");
return view;
} }

视图 /WEB-INF/view/trvc/index.jsp 中的的内容是输出<h2>This is jsp page</h2>

视图 /WEB-INF/view/trvc/index.ftl 中的的内容是输出<h2>This is freemarker page</h2>

总结

本文分析了SpringMVC中的视图机制,View和ViewResolver这两个接口是视图机制的核心,并分析了几个重要的View和ViewResolver接口实现类,最终写了一个区别jsp和freemarker视图的ViewResolver实现类,让读者更加理解视图机制。

希望这篇文章能帮助读者了解SpringMVC视图机制。

文中难免有错误,希望读者能够指明出来。

参考资料

http://my.oschina.net/HeliosFly/blog/221392

SpringMVC视图机制详解[附带源码分析]的更多相关文章

  1. SpringMVC异常处理机制详解[附带源码分析]

    目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...

  2. [转]SpringMVC拦截器详解[附带源码分析]

      目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...

  3. SpringMVC拦截器详解[附带源码分析]

    目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...

  4. SpringMVC拦截器详解[附带源码分析](转)

    本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...

  5. Android应用AsyncTask处理机制详解及源码分析

    1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...

  6. 【转载】Android应用AsyncTask处理机制详解及源码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...

  7. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  8. SpringMVC类型转换、数据绑定详解[附带源码分析]

    目录 前言 属性编辑器介绍 重要接口和类介绍 部分类和接口测试 源码分析 编写自定义的属性编辑器 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那 ...

  9. Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

    目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...

随机推荐

  1. WCF----Stream对象限制操作

    WCF支持Stream操作,尤其对于传递size过大的消息而言,如要考虑传递消息的效率,WCF推荐通过Stream进行操作.然而,WCF Stream操作规定了一些限制,在我们编写相关程序时,需要特别 ...

  2. webrtc中的带宽自适应算法

    转自:http://www.xuebuyuan.com/1248366.html webrtc中的带宽自适应算法分为两种: 1, 发端带宽控制, 原理是由rtcp中的丢包统计来动态的增加或减少带宽,在 ...

  3. ASSM 的三级位图结构

    自动段空间管理(ASSM),它首次出现在Oracle920里(在920以前,段空间的管理方式叫做MSSM,它是由连接列表freelist来完成的,因为freelist存在串行的问题,因此容易引起段头的 ...

  4. opacity

    .css{filter:alpha(opacity:30);/*filter是给IE用到*/opacity:.3; }

  5. PC端重置

    -PC 一,meta <!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta cha ...

  6. Android自动化测试之Monkey Test(一)

    Monkey是什么 Monkey是可以运行在模拟器里或实际设备中的程序.它向系统发送伪随机的用户事件流(如按键输入.触摸屏输入.手势输入等),实现对正在开发的应用程序进行压力测试.   Monkey简 ...

  7. 梳理源码中 View 的工作原理

    欢迎Follow我的GitHub, 关注我的掘金. 在View的工作过程中, 执行三大流程完成显示, 测量(measure)流程, 布局(layout)流程, 绘制(draw)流程. 从perform ...

  8. 70多G的Kindle电子书合集

    70多个G的电子书. 百度网盘分享了几次被屏蔽了, 360网盘每个文件夹最多只能分享2000个文件,所以只能分成多个文件夹分享. 如果大家知道更好的分享方式,欢迎留言相告,多谢: http://yun ...

  9. String的一些总结(JAVA)

    equals: String s1=new String("Hello"); String s2=new String("Hello"); System.out ...

  10. 01 HTML基础

    HTML就是超文本标记语言的简写,是最基础的网页语言. 通过标签定义的语言,代码都是由标签所组成的.(最重要的标签是定义标题.段落和换行的标签) 不区分大小写 head部分是给html页面增加一些辅助 ...