Spring MVC
中,HandlerMethod
类在应用启动过程中搜集Web
控制器方法信息阶段用于记录每个控制器方法的信息,而InvocableHandlerMethod
是HandlerMethod
的扩展,它基于一组HandlerMethodArgumentResolver
从请求上下文中解析出控制器方法的参数值。ServletInvocableHandlerMethod
是对InvocableHandlerMethod
的进一步扩展,扩展了处理控制器方法返回值和注解@ResponseStatus
的能力。
ServletInvocableHandlerMethod
对控制器方法返回值的处理是通过一组注册了的HandlerMethodReturnValueHandler
完成的。
如果返回值是null
或者void
,则ServletInvocableHandlerMethod
会综合考虑@ResponseStatus
注解,not-modified
检查条件,或者控制器方法参数中提供的response
对象然后决定是否将请求设置为已经处理。
如果返回值不是null
/void
,但是有注解@ResponseStatus
并且其属性reason
被设置,请求也将会被设置为已处理。
对于其他返回值情况,ServletInvocableHandlerMethod
会构造一个ModelAndView
对象给调用者。
另外对于浏览器端重定向redirect
,ServletInvocableHandlerMethod
还会考虑对请求属性flash attributes
的更新设置。
1. ServletInvocableHandlerMethod
的应用
ServletInvocableHandlerMethod
具体被使用的位置如下 :
// RequestMappingHandlerAdapter 类代码片段
/**
* Invoke the RequestMapping handler method preparing a ModelAndView
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 对应能够处理当前请求 request 的目标控制器方法 handlerMethod,
// 创建 ServletInvocableHandlerMethod 对象 invocableMethod ,
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置 invocableMethod 的控制器方法参数解析器 (请求上下文->控制器方法参数值)
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置 invocableMethod 的控制器方法返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 通过对ServletInvocableHandlerMethod 对象 invocableMethod方法invokeAndHandle的调用,
// 触发对目标控制器方法参数的解析,目标控制器方法的调用,以及目标控制器方法执行结果的处理
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 如果以上过程中请求已经被标记为完成,这里会返回null,否则会返回一个 ModelAndView 对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
/**
* Create a ServletInvocableHandlerMethod from the given HandlerMethod definition.
* @param handlerMethod the HandlerMethod definition
* @return the corresponding ServletInvocableHandlerMethod (or custom subclass thereof)
* @since 4.2
*/
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(
HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
// 如果带有标记 : 请求已处理,则返回 null ModelAndView
return null;
}
// 如果请求未处理,则构造将要返回的 ModelAndView 对象
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model,
mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
// 针对浏览器端重定向的处理 (redirect) : 重新设置 request 属性
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
2. ServletInvocableHandlerMethod
源代码
package org.springframework.web.servlet.mvc.method.annotation;
/**
* Extends InvocableHandlerMethod with the ability to handle return
* values through a registered HandlerMethodReturnValueHandler and
* also supports setting the response status based on a method-level
* @ResponseStatus annotation.
*
* A null return value (including void) may be interpreted as the
* end of request processing in combination with a @ResponseStatus
* annotation, a not-modified check condition
* (see ServletWebRequest#checkNotModified(long)), or
* a method argument that provides access to the response stream.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
/**
* Creates an instance from the given handler and method.
*/
public ServletInvocableHandlerMethod(Object handler, Method method) {
super(handler, method);
}
/**
* Create an instance from a HandlerMethod.
*/
public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
/**
* Register HandlerMethodReturnValueHandler instances to use to
* handle return values.
*/
public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite
returnValueHandlers) {
this.returnValueHandlers = returnValueHandlers;
}
/**
* Invoke the method and handle the return value through one of the
* configured HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 从请求上下文中解析出控制器方法参数值并调用目标控制器方法
// 方法 invokeForRequest 定义在基类中
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 根据控制器方法注解 @ResponseStatus 中响应状态码信息设置响应状态码,
// 如果没有使用注解 @ResponseStatus,可以将该方法理解为什么都没做
setResponseStatus(webRequest);
// 如果控制器方法执行返回值为null,则综合考虑其他信息考虑是否将请求标记为处理完成
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null ||
mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
// 即使返回值不为 null, 但@ResponseStatus注解属性 reason 被设置,则也将请求处理标记为已经完成
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
// 使用返回值处理器处理控制器方法执行得到的返回值
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
/**
* Set the response status according to the ResponseStatus annotation.
* 根据控制器方法注解 @ResponseStatus 中响应状态码信息设置响应状态码,
* 如果没有使用注解 @ResponseStatus,可以将该方法理解为什么都没做
*/
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
// 获取控制器方法上注解@ResponseStatus的信息,当然也可能没有使用该注解
HttpStatus status = getResponseStatus();
if (status == null) {
// 如果没有使用注解@ResponseStatus,则直接返回
return;
}
// 如果注解@ResponseStatus属性 reason 未被设置 (null 或者 ""),则调用 response.sendError,
// 如果注解@ResponseStatus属性 reason 被设置 (有值的字符串),则调用 response.sendStatus。
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
}
else {
response.setStatus(status.value());
}
}
// To be picked up by RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}
/**
* Does the given request qualify as "not modified"?
* @see ServletWebRequest#checkNotModified(long)
* @see ServletWebRequest#checkNotModified(String)
*/
private boolean isRequestNotModified(ServletWebRequest webRequest) {
return webRequest.isNotModified();
}
private String formatErrorForReturnValue(@Nullable Object returnValue) {
return "Error handling return value=[" + returnValue + "]" +
(returnValue != null ? ", type=" + returnValue.getClass().getName() : "") +
" in " + toString();
}
/**
* Create a nested ServletInvocableHandlerMethod subclass that returns the
* the given value (or raises an Exception if the value is one) rather than
* actually invoking the controller method. This is useful when processing
* async return values (e.g. Callable, DeferredResult, ListenableFuture).
*/
ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
}
/**
* A nested subclass of ServletInvocableHandlerMethod that uses a
* simple {@link Callable} instead of the original controller as the handler in
* order to return the fixed (concurrent) result value given to it. Effectively
* "resumes" processing with the asynchronously produced return value.
*/
private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod {
private final MethodParameter returnType;
public ConcurrentResultHandlerMethod(final Object result,
ConcurrentResultMethodParameter returnType) {
super((Callable<Object>) () -> {
if (result instanceof Exception) {
throw (Exception) result;
}
else if (result instanceof Throwable) {
throw new NestedServletException("Async processing failed", (Throwable) result);
}
return result;
}, CALLABLE_METHOD);
if (ServletInvocableHandlerMethod.this.returnValueHandlers != null) {
setHandlerMethodReturnValueHandlers(
ServletInvocableHandlerMethod.this.returnValueHandlers);
}
this.returnType = returnType;
}
/**
* Bridge to actual controller type-level annotations.
*/
@Override
public Class<?> getBeanType() {
return ServletInvocableHandlerMethod.this.getBeanType();
}
/**
* Bridge to actual return value or generic type within the declared
* async return type, e.g. Foo instead of DeferredResult<Foo>.
*/
@Override
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return this.returnType;
}
/**
* Bridge to controller method-level annotations.
*/
@Override
public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
return ServletInvocableHandlerMethod.this.getMethodAnnotation(annotationType);
}
/**
* Bridge to controller method-level annotations.
*/
@Override
public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
return ServletInvocableHandlerMethod.this.hasMethodAnnotation(annotationType);
}
}
/**
* MethodParameter subclass based on the actual return value type or if
* that's null falling back on the generic type within the declared async
* return type, e.g. Foo instead of DeferredResult<Foo>.
*/
private class ConcurrentResultMethodParameter extends HandlerMethodParameter {
@Nullable
private final Object returnValue;
private final ResolvableType returnType;
public ConcurrentResultMethodParameter(Object returnValue) {
super(-1);
this.returnValue = returnValue;
this.returnType = (returnValue instanceof ReactiveTypeHandler.CollectedValuesList ?
((ReactiveTypeHandler.CollectedValuesList) returnValue).getReturnType() :
ResolvableType.forType(super.getGenericParameterType()).getGeneric());
}
public ConcurrentResultMethodParameter(ConcurrentResultMethodParameter original) {
super(original);
this.returnValue = original.returnValue;
this.returnType = original.returnType;
}
@Override
public Class<?> getParameterType() {
if (this.returnValue != null) {
return this.returnValue.getClass();
}
if (!ResolvableType.NONE.equals(this.returnType)) {
return this.returnType.toClass();
}
return super.getParameterType();
}
@Override
public Type getGenericParameterType() {
return this.returnType.getType();
}
@Override
public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) {
// Ensure @ResponseBody-style handling for values collected from a reactive type
// even if actual return type is ResponseEntity<Flux<T>>
return (super.hasMethodAnnotation(annotationType) ||
(annotationType == ResponseBody.class &&
this.returnValue instanceof ReactiveTypeHandler.CollectedValuesList));
}
@Override
public ConcurrentResultMethodParameter clone() {
return new ConcurrentResultMethodParameter(this);
}
}
}