【SpringMVC(九)】返回值

springmvc最开始支持的是返回一个web页面,主要是jsp。为了支持jsp的渲染,引入了一个ModelAndView类。所以通常情况下,早期的控制器直接返回的就是一个ModelAndView对象。

甚至在DispatcherServlet中,一个handler的返回结果就是ModelAndView类型。

但是,随着后端技术的发展,springmvc更多的是作为一种后端服务的框架,返回值已经不再局限于jsp页面,更多的是接口,注入json之类的返回值。

那么springmvc是如何处理返回值是view还是一个json呢?

在DispatcherServlet的doDispatch方法有有这么一段:

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这个mv就是一个ModelAndView。所以默认所有的handler都是返回ModelAndView的。

再深入看下mv是如何返回的:在ServletInvocableHandlerMethod的invokeAndHandle方法中有这么一段:

try {
    this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}

之后就会调用各种returnValueHandler来处理返回值,比方说处理json的RequestResponseBodyMethodProcessor:

@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException {

	mavContainer.setRequestHandled(true);

	// Try even with null return value. ResponseBodyAdvice could get involved.
	writeWithMessageConverters(returnValue, returnType, webRequest);
}

这里设置了RequestHandled为true。

然后再回到RequestMappingHandlerAdapter中返回mv的一段代码:

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

	modelFactory.updateModel(webRequest, mavContainer);
	if (mavContainer.isRequestHandled()) {
		return null;
	}
	ModelMap model = mavContainer.getModel();
	ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
	if (!mavContainer.isViewReference()) {
		mav.setView((View) mavContainer.getView());
	}
	if (model instanceof RedirectAttributes) {
		Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
	}
	return mav;
}

如果isRequstHandled,那么就直接返回null;所以mv就是null。

再回到DispatcherServlet中doDispatch最后一段代码:

// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
	render(mv, request, response);
	if (errorView) {
		WebUtils.clearErrorRequestAttributes(request);
	}
}

如果mv不为空,才进行后面的render,否则就直接返回了。

所以对于接口类的数据,mv直接是null,所以不会渲染jsp。

如果不为null又是如何渲染呢?

render最终会走到renderMergedOutputModel方法,这是一个抽象方法,有一个常用的实现类是InternalResourceView:

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

	// Expose the model object as request attributes.
	exposeModelAsRequestAttributes(model, request);

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

	// Determine the path for the request dispatcher.
	String dispatcherPath = prepareForRendering(request, response);

	// Obtain a RequestDispatcher for the target resource (typically a JSP).
	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);
	}
}

可以看到最终借助的是Servlet中的RequestDispatcher的forward方法来交给jsp处理的。jsp本质也是servlet。forward方法用于服务器内不同servlet之间进行转发处理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值