假装看源码之springmvc (三) springmvc的视图解析

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方法

         详见:https://blog.csdn.net/shuixiou1/article/details/113006428  

        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<String, Object> 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<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList<ViewResolver>(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 <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> 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";
	}

过程省略......

完毕!!!

猜你喜欢

转载自blog.csdn.net/shuixiou1/article/details/113007614