SpringMVC视图解析原理

源码解析

DispatcherServlet#doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	ModelAndView mv = null;
	// Actually invoke the handler.
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

AbstractHandlerMethodAdapter#handle

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}
RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ModelAndView mav;
	mav = invokeHandlerMethod(request, response, handlerMethod);
	return mav;
}
RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ModelAndViewContainer mavContainer = new ModelAndViewContainer();
	invocableMethod.invokeAndHandle(webRequest, mavContainer);
	return getModelAndView(mavContainer, modelFactory, webRequest);
}
ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	this.returnValueHandlers.handleReturnValue(
			returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
InvocableHandlerMethod#invokeForRequest
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	return doInvoke(args);
}
HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

//ViewNameMethodReturnValueHandler#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue instanceof CharSequence) {
		String viewName = returnValue.toString();
		mavContainer.setViewName(viewName);
		if (isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
}

//ViewNameMethodReturnValueHandler#isRedirectViewName
protected boolean isRedirectViewName(String viewName) {
	return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
}
RequestMappingHandlerAdapter#getModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
		ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
	ModelMap model = mavContainer.getModel();
	ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
	return mav;
}

DispatcherServlet#processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
		@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
		@Nullable Exception exception) throws Exception {
	if (mv != null && !mv.wasCleared()) {
		render(mv, request, response);
	}
}
DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	View view;
	String viewName = mv.getViewName();
	if (viewName != null) {
		// We need to resolve the view name.
		view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
	}
	view.render(mv.getModelInternal(), request, response);
}
DispatcherServlet#resolveViewName
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
		Locale locale, HttpServletRequest request) throws Exception {

	if (this.viewResolvers != null) {
		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
	}
	return null;
}

在这里插入图片描述
有如下5种视图解析器:
ContentNegotiatingViewResolver
BeanNameViewResolver
ThymeleafViewResolver
ViewResolverComposite
InternalResourceViewResolver。
且ContentNegotiatingViewResolver包含了其他4种视图解析器。
视图解析器解析得到视图对象。

ContentNegotiatingViewResolver#resolveViewName
public View resolveViewName(String viewName, Locale locale) throws Exception {
	RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
	Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
	List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
	if (requestedMediaTypes != null) {
		List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
		View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
		if (bestView != null) {
			return bestView;
		}
	}
}
  • ContentNegotiatingViewResolver#getCandidateViews
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
		throws Exception {

	List<View> candidateViews = new ArrayList<>();
	if (this.viewResolvers != null) {
		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				candidateViews.add(view);
			}
		}
	}
	return candidateViews;
}

在这里插入图片描述
ContentNegotiatingViewResolver有如下4种视图解析器:
BeanNameViewResolver
ThymeleafViewResolver
ViewResolverComposite
InternalResourceViewResolver。

对于视图名"redirect:/main.html"
视图名经ThymeleafViewResolver解析,得到RedirectView类的视图对象:RedirectView@7647;
视图名经InternalResourceViewResolver解析,得到RedirectView类的视图对象:RedirectView@7888。
即,得到以上两个候选的视图对象。
在这里插入图片描述
对于视图名"table"
视图名经ThymeleafViewResolver解析,得到Thymeleaf类的视图对象:ThymeleafView@8487。
视图名经InternalResourceViewResolver解析,得到InternalResourceView类的视图对象:InternalResourceView@8501。
即,得到以上两个候选的视图对象。
在这里插入图片描述
对于视图名"forward:/table"
视图名经ThymeleafViewResolver解析,得到InternalResourceView类的视图对象:InternalResourceView@7111。
视图名经InternalResourceViewResolver解析,得到InternalResourceView类的视图对象:InternalResourceView@7132。
即,得到以上两个候选的视图对象。
在这里插入图片描述

  • ContentNegotiatingViewResolver#getBestView
private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) {
	for (View candidateView : candidateViews) {
		if (candidateView instanceof SmartView) {
			SmartView smartView = (SmartView) candidateView;
			if (smartView.isRedirectView()) {
				return candidateView;
			}
		}
	}
}	

getBestView,得到最佳的视图对象。

"redirect:/main.html",得到的最佳视图对象是:RedirectView@7647。
在这里插入图片描述
对于"table",得到的最佳视图对象是:ThymeleafView@8487。
在这里插入图片描述
对于"forward:/table",得到的最佳视图对象是:InternalResourceView@7111。
在这里插入图片描述

AbstractView#render
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
		HttpServletResponse response) throws Exception {
	Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
	prepareResponse(request, response);
	renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
RedirectView#renderMergedOutputModel
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
		HttpServletResponse response) throws IOException {

	String targetUrl = createTargetUrl(model, request);
	targetUrl = updateTargetUrl(targetUrl, model, request, response);

	// Save flash attributes
	RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);

	// Redirect
	sendRedirect(request, response, targetUrl, this.http10Compatible);
}

//RedirectView#sendRedirect
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,
		String targetUrl, boolean http10Compatible) throws IOException {

	String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));
	if (http10Compatible) {
		response.sendRedirect(encodedURL);
	}
}

小结

控制器方法返回的视图名称,

  • 如果是普通字符串,没有前缀,如"table",经视图解析器解析,将返回一个Thymeleaf类型的视图对象,即Thymeleaf视图。
  • 如果字符串前缀是"forward:/xxx",如"forward:/table",经视图解析器解析,将返回一个InternalResourceView类型的视图对象,即InternalResourceView视图(也称 转发视图)。
  • 如果字符串前缀是"redirect:/xxx",如"redirect:/main.html",将返回一个RedirectView类型的视图对象,即RedirectView视图(也称 重定向视图)。

上述所涉内容,文章SpringMVC视图也有过介绍。
本篇源码调试内容基于文章Thymeleaf应用实例

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值