6.视图解析

本文深入探讨SpringMVC框架中的视图解析机制,包括视图解析器如何根据方法返回值获取视图对象,以及视图对象如何进行数据渲染和页面转发或重定向。同时,文章还介绍了视图解析器的缓存机制和国际化资源文件的管理。
摘要由CSDN通过智能技术生成

转发的页面如果在当前项目下,

相对路径回退两层 return"…/…/hello"
forward:转发到一个页面,也可以转发到一个请求,有前缀的转发跟视图解析器无关
redirect:重定向到一个页面,也可以重定向到一个请求,有前缀重定向跟视图解析器无关

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
SpringMVC视图解析;

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();

        这里result=../../hello”
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		
		任何方法的返回值都会包装成modelandView对象
		ModelAndView mav =
		methodInvoker.getModelAndView(
		handlerMethod,  handler.getClass(), 
		result, implicitModel, webRequest);
				
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;

1.任何方法的返回值都会包装成modelandView对象

在这里插入图片描述

  1. processDispatchResult 来到页面的方法
    视图渲染流程:将域中的数据在页面展示
    页面就是用来渲染模型数据的;
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

3.调用render 渲染页面

render(mv, request, response);
mv:要渲染的模型数据/去的页面地址   
request,response:用来转发,重定向
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);

		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 {
			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;
		}
	}

View和ViewResolver;
ViewResolver的作用是根据视图名(方法的返回值)得到View对象

public interface ViewResolver {
    viewName:视图名(方法的返回值)
    得到View对象
	View resolveViewName(String viewName, Locale locale) throws Exception;

}

1.视图解析器得到View对象的流程就是,所有配置的视图解析器都来尝试根据视图名(返回值)得到View对象,如果可以得到就返回,得不到就换下一个视图解析器;
2.调用view对象的render方法

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
			HttpServletRequest request) throws Exception {

		1.遍历所有的ViewResolvers
		现在我们这里只有一个InternalResourceViewResolver
		初始化:ioc容器,默认配置
		for (ViewResolver viewResolver : this.viewResolvers) 

       {
		2.viewResolver视图解析器根据方法的返回值,得到View对象
		     resolveViewName
    View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
		return null;
	}

resolveViewName细节

@Override
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		if (!isCache()) {
			return createView(viewName, locale);
		}
		else {
			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对象;
					返回一个InternalResourceView对象
						view = createView(viewName, locale);


						if (view == null && this.cacheUnresolved) {
							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);
		}
	}

创建View对象
在这里插入图片描述

@Override
	protected View createView(String viewName, Locale locale) throws Exception {
		// If this resolver is not supposed to handle the given view,
		// return null to pass on to the next resolver in the chain.
		if (!canHandle(viewName, locale)) {
			return null;
		}
		// Check for special "redirect:" prefix.
		if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
			String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
			
			如果前缀是redirect,就创建出一个RedirectView对象
			RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
			
			return applyLifecycleMethods(viewName, view);
		}
		// Check for special "forward:" prefix.
		
         if (viewName.startsWith(FORWARD_URL_PREFIX)) {
			String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
			
			如果前缀是forward,就创建出一个InternalResourceView对象
			return new InternalResourceView(forwardUrl);
		}
		// Else fall back to superclass implementation: calling loadView.
		如果没有前缀,就使用父类默认创建一个View对象
		return super.createView(viewName, locale);
	}
	@Override
	public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
				" and static attributes " + this.staticAttributes);
		}

		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		
		prepareResponse(request, response);
		
		渲染要给页面输出的所有数据
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}

InternalResourceView有这个方法renderMergedOutputModel

protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		将模型中的数据放在请求域中,
		exposeModel AsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		exposeHelpers(request);

		要转发的页面
		String dispatcherPath = prepareForRendering(request, response);

		转发器
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.include(request, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			转发
			rd.forward(request, response);
		}
	}

将模型中的所有数据取出来全放在request域中

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
		for (Map.Entry<String, Object> entry : model.entrySet()) {
			取出隐含模型的key
			String modelName = entry.getKey();
			取出隐含模型的value
			Object modelValue = entry.getValue();
			if (modelValue != null) {
				将模型中的所有数据取出来全放在request域中
				request.setAttribute(modelName, modelValue);
				if (logger.isDebugEnabled()) {
					logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
							"] to request in view with name '" + getBeanName() + "'");
				}
			}
			else {
				request.removeAttribute(modelName);
				if (logger.isDebugEnabled()) {
					logger.debug("Removed model object '" + modelName +
							"' from request in view with name '" + getBeanName() + "'");
				}
			}
		}
	}

视图解析器只是为了得到视图对象,
视图对象才能真正的转发(将模型数据全部放在请求域中)或者重定向到页面
视图对象才能真正的渲染视图。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
模型数据输出为json格式
在这里插入图片描述
在这里插入图片描述
谁先解析,谁后解析

jstlView 国际化

SpringMVC管理国际化资源文件;配置一个资源文件管理器
    <bean id="messageSource"
    class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="il8n" />
  </bean>

自定义:
视图解析器根据方法的返回值(viewName)得到视图对象View

多个视图解析器都会尝试能否得到视图对象

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
			HttpServletRequest request) throws Exception {

		1.多个视图解析器都会尝试能否得到视图对象
		for (ViewResolver viewResolver : this.viewResolvers) 

       {
		2.viewResolver视图解析器根据方法的返回值,得到View对象    
    View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
		return null;
	}

在这里插入图片描述
视图对象不同就可以具有不同渲染功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值