博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
假装看源码之springmvc (三) springmvc的视图解析
阅读量:4074 次
发布时间:2019-05-25

本文共 6751 字,大约阅读时间需要 22 分钟。

springmvc的视图解析

概述:springmvc的视图解析主要是通过ViewResolver构建View,然后组装model数据,最后渲染视图,ViewResolver是由我们配置的,默认提供的ViewResolver是InternalResourceViewResolver即Jsp的视图,下面我们在源码里找到视图解析的原理和实现。

一、ViewResolver分析

springmvc视图解析是通过ViewResolver根据view的名称,来查询指定的view,而View复制渲染,Resolver复制查找指定的view,默认情况下配置的ViewResolver是InternalResourceViewResolver,对应InternalResourceViewResolver查询出的view是InternalResourceView的子类JstlView.class。

1、ViewResolver 的继承结构

        ViewResolver 顶级接口

            + View resolveViewName(String viewName, Locale locale)

        AbstractCachingViewResolver 缓存抽象基类---主要功能是View对象加缓存访问的处理
            + View resolveViewName(String viewName, Locale locale) // 缓存处理,内部调用createView()创建view
            View createView(String viewName) // 创建视图方法,直接调用loadView创建view
            abstract View loadView(String viewNmae) // 抽象方法-子类实现
        
        UrlBasedViewResolver  Url解析的基础实现类---主要功能就是处理redirect和forword,以及根据class创建View设置url
            View createView(String viewName) // 复写父类增加解析redirect和forword处理后,再次调用父类的createView方法。
            View loadView(String viewName) // 主要调用buildView实现
            View buildView(String viewName) // 主要根据getViewClass创建view实例,并且必须是AbstractUrlBasedView的子类
        
        InternalResourceViewResolver 继承UrlBasedViewResolver---处理jsp的视图解析, 功能就是提供getViewClass()
        
        AbstractTemplateViewResolver 模板视图解析的基类 --- 主要就是覆写buildView并限制getViewClass类型和公共设置
            View buildView(String viewName) // 覆写buildView,并调用父类的buildView后,限制AbstractTemplateView的子类
        
        FreeMarkerViewResolver 继承AbstractTemplateViewResolver---功能就是提供FreeMarker的ViewClass()

2、类图和介绍

3、ViewResolver主要的代码片段

        1) AbstractCachingViewResolver实现的resolveViewName方法

         详见:  

        2) UrlBasedViewResolver的createView方法解析redirect和forward。

protected View createView(String viewName, Locale locale) throws Exception {			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);			}			if (viewName.startsWith(FORWARD_URL_PREFIX)) {				String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());				return new InternalResourceView(forwardUrl);			}			// 调用父类createView,实际调用模板方法loadView由本类是实现,再调用buildView			return super.createView(viewName, locale);		}

     3) UrlBasedViewResolver的buildView方法

protected AbstractUrlBasedView buildView(String viewName) throws Exception {			AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());  // 根据viewClass创建View			view.setUrl(getPrefix() + viewName + getSuffix()); // 设置url				String contentType = getContentType();			if (contentType != null) {				view.setContentType(contentType);			}			// .....略			return view;		}

    4) InternalResourceViewResolver和FreeMarkerViewResolver的设置viewClass

public InternalResourceViewResolver() { // 构造函数设置ViewClass				Class
viewClass = requiredViewClass(); if (viewClass.equals(InternalResourceView.class)) { viewClass = JstlView.class; } setViewClass(viewClass); } protected Class
requiredViewClass() { // 提供InternalResourceView视图 return InternalResourceView.class; } public FreeMarkerViewResolver() { // 构造函数设置ViewClass setViewClass(requiredViewClass()); } protected Class
requiredViewClass() { // 提供FreeMarkerView的视图 return FreeMarkerView.class; }

二、View类分析

1、概述:从上面ViewResolver的分析中我们可以知道,ViewResolver获取view时,已经创建了view的实例,并且处理了redirect和forword等请求,设置了url,现在如果要渲染页面,只需要把model的数据和视图进行组合,然后渲染页面即可。

      View的类型和ViewResolver成对应出现的,最终的ViewResolver都指定了具体的View类型,我们先看View的继承结构。

2、View的类结构

        View 视图接口

             + void render(model, request, response)

        AbstractView 抽象基类

            + void render(model, request, response)  // 实现render主要是一些model数据的合并,调用模板方法renderMergedOutputModel
            abstract renderMergedOutputModel() // model数据合并后,执行渲染的模板方法,子类实现。
            
        AbstractUrlBasedView 抽象基类 --- 提供Url内置属性  
        
        InternalResourceView JSP的视图实现 --- 转发请求到jsp
            + renderMergedOutputModel() // Model数据添加到requestAttr中,RequestDispatcher转发请求到jsp页面

        RedirectView 重定向操作用视图的包装 ---- 主要是重定向及参数处理以及处理FlashMap

            + renderMergedOutputModel() // 重定向参数处理以及FlashMap的处理。

  3、源码片段

   InternalResourceView的renderMergedOutputModel()方法

   protected void renderMergedOutputModel(                Map
model, HttpServletRequest request, HttpServletResponse response) throws Exception {                                exposeModelAsRequestAttributes(model, request); // model参数到requestAttr                String dispatcherPath = prepareForRendering(request, response); // 路径处理                RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); // request.getRequestDispatcher(path);                rd.forward(request, response); // 转发            }

三、流程分析

对ViewResolver和View的基本功能和类结构了解后,基本就知道,spring-mvc视图解析,就是一个对应的视图解析器,根据视图名称创建对应类型视图的过程,这个过程处理了视图的缓存,redirect和forword,并且最终会组装model参数,或者处理重定向FlashMap的参数到session等等一系列操作。

下面我们对照执行过程中的源码了解一下springmvc的视图解析。

3.1 首先,我们先看下视图解析器ViewResolver如何初始的,即springmvc根据什么来确定视图解析器的类型,直接切入主题,我们到spring-mvc核心servlet-DispatcherServlet中初始化ViewResolver中查看。

private void initViewResolvers(ApplicationContext context) {		this.viewResolvers = null;		if (this.detectAllViewResolvers) {			// 从spring容器查询所有的ViewResolvers			Map
matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList
(matchingBeans.values()); OrderComparator.sort(this.viewResolvers); } } else { // 查询一个ViewResolvers ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); this.viewResolvers = Collections.singletonList(vr); } // 如果没有,就加载默认,默认实际上是DispatcherServlet.properties中配置了 if (this.viewResolvers == null) { this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); } } }
protected 
List
getDefaultStrategies(ApplicationContext context, Class
strategyInterface) { String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); // 从配置文件得到指定的key if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); for (String className : classNames) { Class
clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); // 获取class,并后续加载到spring容器创建bean // ......... } }
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

 上面是DispatcherServlet.properties指定的默认视图解析器

3.2 实际追踪请求过程

先创建一个Controller

/**	 * 基本请求	 */	@RequestMapping("/hello")	public String baseHello(Model model) {				model.addAttribute("param1", "param1");		model.addAttribute("param2", "param2");				return "/hello";	}		/**	 * 重定向请求	 */	@RequestMapping("/redirect")	public String redireHello(Model model) {				return "redirect:/hello";	}

过程省略......

完毕!!!

 

 

 

转载地址:http://zruni.baihongyu.com/

你可能感兴趣的文章
join left right inner full 区别
查看>>
explain 索引实践
查看>>
hibernate & mybatis 比较
查看>>
nibernate 一级缓存与二级缓存
查看>>
MyISAM和InnoDB的主要区别和应用场景
查看>>
mysql 索引优化 实战概述
查看>>
一次子线程事务回滚实践笔记-编程式事务
查看>>
子线程异常抛出 及 主线程事务回滚
查看>>
spring boot 定时任务
查看>>
HttpClient 4.x 多线程
查看>>
copyonwrite arraylist
查看>>
CopyOnWriteArrayList 的set为什么要复制?扩容为什么一个一个来,而不是1.5倍
查看>>
由CopyOnWriteArrayList类的set方法引发对volatile深入理解
查看>>
jdk并发容器整理(yet)
查看>>
看看源码怎么处理,Java中Hashtable,Hashmap,ConcurrentHashMap,Key Value为null时
查看>>
HashSet原理 与 linkedHashSet
查看>>
单例模式的一种实现
查看>>
一次redis悲观锁 实现 微信jssdk token缓存
查看>>
常见的几种单例模式
查看>>
一张图看懂encodeURI、encodeURIComponent、decodeURI、decodeURIComponent的区别
查看>>