Consistent InvocableHandlerMethod implementations
This commit makes the 3 existing InvocableHandlerMethod types more consistent and comparable with each other. 1. Use of consistent method names and method order. 2. Consistent error formatting. 3. Explicit for loops for resolving argument values in webflux variant because that makes it more readable, creates less garabage, and it's the only way to bring consistency since the other two variants cannot throw exceptions inside Optional lambdas (vs webflux variant which can wrap it in a Mono). 4. Use package private HandlerMethodArgumentComposite in webflux variant in order to pick up the resolver argument caching that the other two variants have. 5. Polish tests. 6. Add missing tests for messaging variant.
This commit is contained in:
@@ -31,7 +31,8 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
|
||||
* Resolves method parameters by delegating to a list of registered
|
||||
* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
|
||||
* Previously resolved method parameters are cached for faster lookups.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -62,9 +63,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
*/
|
||||
public HandlerMethodArgumentResolverComposite addResolvers(@Nullable HandlerMethodArgumentResolver... resolvers) {
|
||||
if (resolvers != null) {
|
||||
for (HandlerMethodArgumentResolver resolver : resolvers) {
|
||||
this.argumentResolvers.add(resolver);
|
||||
}
|
||||
Collections.addAll(this.argumentResolvers, resolvers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -76,9 +75,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
@Nullable List<? extends HandlerMethodArgumentResolver> resolvers) {
|
||||
|
||||
if (resolvers != null) {
|
||||
for (HandlerMethodArgumentResolver resolver : resolvers) {
|
||||
this.argumentResolvers.add(resolver);
|
||||
}
|
||||
this.argumentResolvers.addAll(resolvers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -100,17 +97,20 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
|
||||
|
||||
/**
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
* Whether the given {@linkplain MethodParameter method parameter} is
|
||||
* supported by any registered {@link HandlerMethodArgumentResolver}.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (getArgumentResolver(parameter) != null);
|
||||
return getArgumentResolver(parameter) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over registered {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} and invoke the one that supports it.
|
||||
* @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
|
||||
* Iterate over registered
|
||||
* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} and
|
||||
* invoke the one that supports it.
|
||||
* @throws IllegalStateException if no suitable
|
||||
* {@link HandlerMethodArgumentResolver} is found.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@@ -119,13 +119,16 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
|
||||
|
||||
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
|
||||
if (resolver == null) {
|
||||
throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
|
||||
" supportsParameter should be called first.");
|
||||
}
|
||||
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
|
||||
* Find a registered {@link HandlerMethodArgumentResolver} that supports
|
||||
* the given method parameter.
|
||||
*/
|
||||
@Nullable
|
||||
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
@@ -35,14 +36,9 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
/**
|
||||
* Provides a method for invoking the handler method for a given request after resolving its
|
||||
* method argument values through registered {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
|
||||
*
|
||||
* <p>Argument resolution often requires a {@link WebDataBinder} for data binding or for type
|
||||
* conversion. Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply
|
||||
* a binder factory to pass to argument resolvers.
|
||||
*
|
||||
* <p>Use {@link #setHandlerMethodArgumentResolvers} to customize the list of argument resolvers.
|
||||
* Extension of {@link HandlerMethod} that invokes the underlying method with
|
||||
* argument values resolved from the current HTTP request through a list of
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
@@ -50,10 +46,13 @@ import org.springframework.web.method.HandlerMethod;
|
||||
*/
|
||||
public class InvocableHandlerMethod extends HandlerMethod {
|
||||
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
|
||||
@Nullable
|
||||
private WebDataBinderFactory dataBinderFactory;
|
||||
|
||||
private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
|
||||
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
|
||||
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
@@ -99,7 +98,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
* Set {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} to use to use for resolving method argument values.
|
||||
*/
|
||||
public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {
|
||||
this.argumentResolvers = argumentResolvers;
|
||||
this.resolvers = argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,64 +150,59 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
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);
|
||||
args[i] = resolveProvidedArgument(parameter, providedArgs);
|
||||
args[i] = findProvidedArgument(parameter, providedArgs);
|
||||
if (args[i] != null) {
|
||||
continue;
|
||||
}
|
||||
if (this.argumentResolvers.supportsParameter(parameter)) {
|
||||
try {
|
||||
args[i] = this.argumentResolvers.resolveArgument(
|
||||
parameter, mavContainer, request, this.dataBinderFactory);
|
||||
continue;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Leave stack trace for later, e.g. AbstractHandlerExceptionResolver
|
||||
if (logger.isDebugEnabled()) {
|
||||
String message = ex.getMessage();
|
||||
if (message != null && !message.contains(parameter.getExecutable().toGenericString())) {
|
||||
logger.debug(formatArgumentError(parameter, message));
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
if (args[i] == null) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
|
||||
if (!ObjectUtils.isEmpty(providedArgs)) {
|
||||
for (Object providedArg : providedArgs) {
|
||||
if (parameter.getParameterType().isInstance(providedArg)) {
|
||||
return providedArg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String formatArgumentError(MethodParameter param, String message) {
|
||||
return "Could not resolve parameter [" + param.getParameterIndex() + "] in " +
|
||||
param.getExecutable().toGenericString() + (StringUtils.hasText(message) ? ": " + message : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to resolve a method parameter from the list of provided argument values.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolveProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
|
||||
if (providedArgs == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object providedArg : providedArgs) {
|
||||
if (parameter.getParameterType().isInstance(providedArg)) {
|
||||
return providedArg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoke the handler method with the given argument values.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(Object... args) throws Exception {
|
||||
ReflectionUtils.makeAccessible(getBridgedMethod());
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user