Spring MVC : 概念模型 InvocableHandlerMethod
InvocableHandlerMethod是对HandlerMethod的扩展,基于一组HandlerMethodArgumentResolver从请求上下文中解析出控制器方法的参数值,然后调用控制器方法。除此之外,InvocableHandlerMethod和HandlerMethod的不同在于:HandlerMethod被容器在启动过程中搜集控制器方法阶段用于记录每个控制器方法,而I...
·
InvocableHandlerMethod
是对HandlerMethod
的扩展,基于一组HandlerMethodArgumentResolver
从请求上下文中解析出控制器方法的参数值,然后调用控制器方法。
除此之外,InvocableHandlerMethod
和HandlerMethod
的不同在于:
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
更多推荐
已为社区贡献14条内容
所有评论(0)