Spring MVC : 概念模型 ServletInvocableHandlerMethod

本文深入探讨了SpringMVC框架中ServletInvocableHandlerMethod类的作用和工作原理,它是如何处理控制器方法参数和返回值,以及与@ResponseStatus注解的交互。此外,还详细介绍了其在RequestMappingHandlerAdapter中的应用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring MVC中,HandlerMethod类在应用启动过程中搜集Web控制器方法信息阶段用于记录每个控制器方法的信息,而InvocableHandlerMethodHandlerMethod的扩展,它基于一组HandlerMethodArgumentResolver从请求上下文中解析出控制器方法的参数值。ServletInvocableHandlerMethod是对InvocableHandlerMethod的进一步扩展,扩展了处理控制器方法返回值和注解@ResponseStatus的能力。

ServletInvocableHandlerMethod对控制器方法返回值的处理是通过一组注册了的HandlerMethodReturnValueHandler完成的。

如果返回值是null或者void,则ServletInvocableHandlerMethod会综合考虑@ResponseStatus注解,not-modified检查条件,或者控制器方法参数中提供的response对象然后决定是否将请求设置为已经处理。

如果返回值不是null/void,但是有注解@ResponseStatus并且其属性reason被设置,请求也将会被设置为已处理。

对于其他返回值情况,ServletInvocableHandlerMethod会构造一个ModelAndView对象给调用者。

另外对于浏览器端重定向redirectServletInvocableHandlerMethod还会考虑对请求属性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);
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值