spring基本使用(26)-springMVC10-SpringMVC的视图解析器ViewResolver

1、什么是视图解析器?视图解析器的作用是啥?视图解析器在整个请求的的过程中执行时机在哪里?

      答:视图解析器就是SpringMVC中定义视图的信息解析的组件。

             视图解析器的作用就是根据视图名称viewName + 本地化信息Locale来解析出一个View视图。

             视图解析器的执行时机在,HandlerMappingAdapter执行结束后会得到一个ModelAndView类型的实例,然后视图解析器会根据这个ModelAndView中封装的信息来解析出一个View实例。

 

2、ViewResolver接口定义以及类图:

public interface ViewResolver {

	/**
	 * Resolve the given view by name.
	 * <p>Note: To allow for ViewResolver chaining, a ViewResolver should
	 * return {@code null} if a view with the given name is not defined in it.
	 * However, this is not required: Some ViewResolvers will always attempt
	 * to build View objects with the given name, unable to return {@code null}
	 * (rather throwing an exception when View creation failed).
	 * @param viewName name of the view to resolve
	 * @param locale Locale in which to resolve the view.
	 * ViewResolvers that support internationalization should respect this.
	 * @return the View object, or {@code null} if not found
	 * (optional, to allow for ViewResolver chaining)
	 * @throws Exception if the view cannot be resolved
	 * (typically in case of problems creating an actual View object)
	 */
    根据view的名称 + 本次请求的本地化信息实例 来解析出一个View实例。
	View resolveViewName(String viewName, Locale locale) throws Exception;

}

     类图的结构比较庞大,我们挑重要的进行展示即可:

            

 

3、常用的视图解析器InternalResourceViewResolver配置方式:

      InternalResourceViewResolver这个是我们常用的视图解析器,其配置方式如下: 

    <!-- 配置一个试图解析器,浅前缀是我们存放jsp的目录,后缀是指定类型是.jsp -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver" >
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"/>

    </bean>

      这样配置就是在解析视图的时候,会使用prefix + ViewName + suffix  来定位到jsp文件,然后将jsp文件进行解析,然后生成一个View实例,View也是一个接口,不同实现的ViewResolver解析的View类型也是不一样的,View的实现也是也别丰富,比如有JstlView(可进行jstl标签转换的视图)、FreeMarkView(基于FreeMark进行模板替换的视图)、VelocityView等实现。

 

4、ViewResolver在整个请求的执行时机解析:

       案例:这个案例跟之前一篇讲解返回值处理器的时候是一样的,注意@ResponseBody这种返回值方式是不会有视图解析的,在讲解具体的返回值处理器实现类RequestResponseBodyMethodProcessor的时候有明确的实现解析。

     @RequestMapping("test/save")
     public String test(@Validated(value = HumaSaveGroup.class)  Huma huma, BindingResult bindingResult) {
           huma.setAge(18);
           return "register"; //这个register是一个viewName,比如jsp的名称
      }

             案例中返回的“register”就是我们定义的一个jsp文件的名称,它的路径如下图:

                       

 

         开始解析ViewResolver执行点:在之前解析返回值处理器的时候我们讲到了RequestMappingHnadlerAdapter的invokeHandlerMethod(...)方法,我们今天依旧从这里开始,核心代码:

	获取一个ModelAndView实例并返回,这里就是整个RequestMappingHnadlerAdapter的结果返回实现。
    return getModelAndView(mavContainer, modelFactory, webRequest);

            获取ModelAndView实例的实现如下:

	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

        1、根据ModelAndViewContainer实例里的model数据取更新请求里的属性数据,也就是将需要存爱Sesssion里面的model进行存储等操作。
		modelFactory.updateModel(webRequest, mavContainer);

        2、如果本次请求是已经处理结束了的,就返回null,之前说的@ResponseBody的这种方式在此处就不会返回一个ModelAndView实例,而是直接返回null.
		if (mavContainer.isRequestHandled()) {
			return null;
		}

        3、如果请求并没有处理结束,就获取ModelAndViewContainer 实例里的model数据。
		ModelMap model = mavContainer.getModel();

        4、viewName是在返回值处理器里面设置的,在ModelAndViewContainer中view是一个Object类型在
返回值处理器中设置的时候其实view属性就是一个String类型的viewName,使用其 + model数据构建一个
ModelAndView实例。
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());

        5、如果ModelAndViewContainer实例中的view属性不是String类型的,那就设置ModelAndView实例的view是ModelAndViewContainer实例的view实例。
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}

        6、如果model实例的类型是RedirectAttributes重定向相关的类型就进行一些处理,此处不是核心暂且跳过。
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
        7、最终返回一个ModelAndView实例。
		return mav;
	}

                   有了HandlerMappingHandlerAdapter返回的ModelAndView实例后,我们来到DispatcherServlet的doDispatch(...)方法中继续整个请求的流程:

                ...
               此处mv就是返回的ModelAndView实例。
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

                处理器默认的viewName,就是判断ModelAndView实例中是否有view属性,如果没有的话就设置为配置的默认发ViewName
				applyDefaultViewName(processedRequest, mv);

                执行mvc拦截器的后置处理方法。
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
            处理器结果,此处重点,视图解析器、异常处理器都是在此方法中工作。
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

                    我们来到:processDispatchResult(...)方法:

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;

        1、如果在HandelrMethod查找中+执行处理器过程中产生异常的话就进行异常处理,这个在下一篇文章给你剖析全局异常处理器的时候会进行详细介绍。
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

        2、如果执行正常且ModelAndView实例不为空 且 ModelAndView是没有被清除的默认就是不被清除的。
		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
            
            那就呈献视图!!!!!!
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

                   呈献视图:render(...)实现:

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
        1、先使用本地化信息解析器解析到当前的本地化信息实例Locale。
		Locale locale = this.localeResolver.resolveLocale(request);

        2、设置响应实例的本地化信息。
		response.setLocale(locale);

        3、使用视图解析器解析出一个View实例,这里就是视图解析器的工作点。
		View view;
		if (mv.isReference()) {
			// We need to resolve the view name.
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}

            4、解析成视图后进行视图呈献,就是输出到响应中,然后借结束请求。
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
						getServletName() + "'", ex);
			}
			throw ex;
		}
	}

 

5、解析了ViewResolver的执行点后,我们知道了整体的工作流程,至于后面的解析视图、视图呈献的原理这里就不展开了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值