InvocableHandlerMethod是对HandlerMethod的扩展,基于一组HandlerMethodArgumentResolver从请求上下文中解析出控制器方法的参数值,然后调用控制器方法。

除此之外,InvocableHandlerMethodHandlerMethod的不同在于:

  • HandlerMethod被容器在启动过程中搜集控制器方法阶段用于记录每个控制器方法,
  • InvocableHandlerMethod用于在某个请求被控制器方法处理时,包装处理所需的各种参数和执行处理逻辑。

源代码分析

package org.springframework.web.method.support;

/**
 * Extension of HandlerMethod that invokes the underlying method with
 * argument values resolved from the current HTTP request through a list of
 * HandlerMethodArgumentResolver.
 *
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @since 3.1
 */
public class InvocableHandlerMethod extends HandlerMethod {

	private static final Object[] EMPTY_ARGS = new Object[0];


	@Nullable
	private WebDataBinderFactory dataBinderFactory;

    // 一组控制器方法参数值解析器,用于从请求上下文信息中解析控制器方法参数对应的参数值
	private HandlerMethodArgumentResolverComposite resolvers = 
		new HandlerMethodArgumentResolverComposite();

    // 用于发现控制器方法参数的名称,参数名称在从请求上下文信息中解析控制器方法参数值时会被使用
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


	/**
	 * 基于一个 HandlerMethod 构造一个 InvocableHandlerMethod 对象,
     * 通常用在请求处理过程中,根据请求信息找到了相应的 HandlerMethod,然后准备调用底层的控制器方法了,
     * 此时调用该构造函数形成一个 InvocableHandlerMethod 实例,然后传递其它必要的参数给该 
     * InvocableHandlerMethod 实例,然后执行目标控制器方法的调用。
     * Create an instance from a HandlerMethod.
	 */
	public InvocableHandlerMethod(HandlerMethod handlerMethod) {
		super(handlerMethod);
	}

	/**
	 * Create an instance from a bean instance and a method.
     * 基于一个 Web 控制器对象和相应的控制器方法构造一个 InvocableHandlerMethod 对象
	 */
	public InvocableHandlerMethod(Object bean, Method method) {
		super(bean, method);
	}

	/**
	 * Construct a new handler method with the given bean instance, method name and parameters.
	 * @param bean the object bean
	 * @param methodName the method name
	 * @param parameterTypes the method parameter types
	 * @throws NoSuchMethodException when the method cannot be found
	 */
	public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
			throws NoSuchMethodException {

		super(bean, methodName, parameterTypes);
	}


	/**
	 * Set the WebDataBinderFactory to be passed to argument resolvers allowing them to create
	 * a WebDataBinder for data binding and type conversion purposes.
	 * @param dataBinderFactory the data binder factory.
	 */
	public void setDataBinderFactory(WebDataBinderFactory dataBinderFactory) {
		this.dataBinderFactory = dataBinderFactory;
	}

	/**
	 * Set HandlerMethodArgumentResolver HandlerMethodArgumentResolvers to use to use for resolving 
	 * method argument values.
	 * 设置所需要的控制器方法参数解析器
	 */
	public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite 
		argumentResolvers) {
		this.resolvers = argumentResolvers;
	}

	/**
	 * Set the ParameterNameDiscoverer for resolving parameter names when needed
	 * (e.g. default request attribute name).
     * 设置参数名称发现器
	 * Default is a org.springframework.core.DefaultParameterNameDiscoverer.
	 */
	public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
		this.parameterNameDiscoverer = parameterNameDiscoverer;
	}


	/**
	 * Invoke the method after resolving its argument values in the context of the given request.
	 * Argument values are commonly resolved through
	 *  HandlerMethodArgumentResolver HandlerMethodArgumentResolvers.
	 * The providedArgs parameter however may supply argument values to be used directly,
	 * i.e. without argument resolution. Examples of provided argument values include a
	 * WebDataBinder, a SessionStatus, or a thrown exception instance.
	 * Provided argument values are checked before argument resolvers.
	 * Delegates to #getMethodArgumentValues and calls #doInvoke with the
	 * resolved arguments.
	 * @param request the current request
	 * @param mavContainer the ModelAndViewContainer for this request
	 * @param providedArgs "given" arguments matched by type, not resolved
	 * @return the raw value returned by the invoked method
	 * @throws Exception raised if no suitable argument resolver can be found,
	 * or if the method raised an exception
	 * @see #getMethodArgumentValues
	 * @see #doInvoke
	 */
	@Nullable
	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);
	}

	/**
	 * Get the method argument values for the current request, checking the provided
	 * argument values and falling back to the configured argument resolvers.
	 * The resulting array will be passed into #doInvoke.
	 * @since 5.1.2
	 */
	protected Object[] getMethodArgumentValues(NativeWebRequest request, 
            @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

		if (ObjectUtils.isEmpty(getMethodParameters())) {
        // 控制器方法没有参数的情况
			return EMPTY_ARGS;
		}
		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
           // 获取一个控制器方法参数 
			MethodParameter parameter = parameters[i];
            // 发现控制器方法参数的名称
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            // 看看针对该参数,有没有通过 providedArgs 提供的参数值
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
            // 如果针对该参数,没有通过 providedArgs 提供的参数值,则尝试使用属性 resolvers,
            // 其实是一组 HandlerMethodArgumentResolver 实例的一个组合对象,从请求上下文中解析
            // 该参数的参数值
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, 
					"No suitable resolver"));
			}
			try {
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, 
					this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled..
				if (logger.isDebugEnabled()) {
					String error = ex.getMessage();
					if (error != null 
						&& !error.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, error));
					}
				}
				throw ex;
			}
		}
		return args;
	}

	/**
	 * Invoke the handler method with the given argument values.
	 */
	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(text, args), ex);
		}
		catch (InvocationTargetException ex) {
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) {
				throw (RuntimeException) targetException;
			}
			else if (targetException instanceof Error) {
				throw (Error) targetException;
			}
			else if (targetException instanceof Exception) {
				throw (Exception) targetException;
			}
			else {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), 
					targetException);
			}
		}
	}

}

参考文章

Spring MVC : 概念模型 HandlerMethod
Spring MVC : 概念模型 HandlerMethodArgumentResolver

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐