diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java index fa8ed9877fe..0be63540cd3 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java @@ -18,6 +18,8 @@ package org.springframework.messaging.handler; import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,6 +33,8 @@ import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Encapsulates information about a handler method consisting of a @@ -321,6 +325,59 @@ public class HandlerMethod { } + // Support methods for use in "InvocableHandlerMethod" sub-class variants.. + + @Nullable + protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) { + if (!ObjectUtils.isEmpty(providedArgs)) { + for (Object providedArg : providedArgs) { + if (parameter.getParameterType().isInstance(providedArg)) { + return providedArg; + } + } + } + return null; + } + + protected static String formatArgumentError(MethodParameter param, String message) { + return "Could not resolve parameter [" + param.getParameterIndex() + "] in " + + param.getExecutable().toGenericString() + (StringUtils.hasText(message) ? ": " + message : ""); + } + + /** + * Assert that the target bean class is an instance of the class where the given + * method is declared. In some cases the actual endpoint instance at request- + * processing time may be a JDK dynamic proxy (lazy initialization, prototype + * beans, and others). Endpoint classes that require proxying should prefer + * class-based proxy mechanisms. + */ + protected void assertTargetBean(Method method, Object targetBean, Object[] args) { + Class methodDeclaringClass = method.getDeclaringClass(); + Class targetBeanClass = targetBean.getClass(); + if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { + String text = "The mapped handler method class '" + methodDeclaringClass.getName() + + "' is not an instance of the actual endpoint bean class '" + + targetBeanClass.getName() + "'. If the endpoint requires proxying " + + "(e.g. due to @Transactional), please use class-based proxying."; + throw new IllegalStateException(formatInvokeError(text, args)); + } + } + + protected String formatInvokeError(String text, Object[] args) { + + String formattedArgs = IntStream.range(0, args.length) + .mapToObj(i -> (args[i] != null ? + "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : + "[" + i + "] [null]")) + .collect(Collectors.joining(",\n", " ", " ")); + + return text + "\n" + + "Endpoint [" + getBeanType().getName() + "]\n" + + "Method [" + getBridgedMethod().toGenericString() + "] " + + "with argument values:\n" + formattedArgs; + } + + /** * A MethodParameter with HandlerMethod-specific behavior. */ diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java index ebea311754f..e17287de416 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java @@ -20,8 +20,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; @@ -32,7 +30,6 @@ import org.springframework.messaging.Message; import org.springframework.messaging.handler.HandlerMethod; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; /** * Extension of {@link HandlerMethod} that invokes the underlying method with @@ -163,23 +160,6 @@ public class InvocableHandlerMethod extends HandlerMethod { 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 : ""); - } - /** * Invoke the handler method with the given argument values. */ @@ -212,39 +192,6 @@ public class InvocableHandlerMethod extends HandlerMethod { } } - /** - * Assert that the target bean class is an instance of the class where the given - * method is declared. In some cases the actual endpoint instance at request- - * processing time may be a JDK dynamic proxy (lazy initialization, prototype - * beans, and others). Endpoint classes that require proxying should prefer - * class-based proxy mechanisms. - */ - private void assertTargetBean(Method method, Object targetBean, Object[] args) { - Class methodDeclaringClass = method.getDeclaringClass(); - Class targetBeanClass = targetBean.getClass(); - if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { - String text = "The mapped handler method class '" + methodDeclaringClass.getName() + - "' is not an instance of the actual endpoint bean class '" + - targetBeanClass.getName() + "'. If the endpoint requires proxying " + - "(e.g. due to @Transactional), please use class-based proxying."; - throw new IllegalStateException(formatInvokeError(text, args)); - } - } - - private String formatInvokeError(String text, Object[] args) { - - String formattedArgs = IntStream.range(0, args.length) - .mapToObj(i -> (args[i] != null ? - "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : - "[" + i + "] [null]")) - .collect(Collectors.joining(",\n", " ", " ")); - - return text + "\n" + - "Endpoint [" + getBeanType().getName() + "]\n" + - "Method [" + getBridgedMethod().toGenericString() + "] " + - "with argument values:\n" + formattedArgs; - } - MethodParameter getAsyncReturnValueType(@Nullable Object returnValue) { return new AsyncResultMethodParameter(returnValue); } diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java index 0ae0c1f5eec..b6c3883fec2 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -21,6 +21,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -36,6 +38,8 @@ import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ResponseStatus; /** @@ -172,7 +176,6 @@ public class HandlerMethod { this.resolvedFromHandlerMethod = handlerMethod; } - private MethodParameter[] initMethodParameters() { int count = this.bridgedMethod.getParameterCount(); MethodParameter[] result = new MethodParameter[count]; @@ -390,6 +393,57 @@ public class HandlerMethod { } + // Support methods for use in "InvocableHandlerMethod" sub-class variants.. + + @Nullable + protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) { + if (!ObjectUtils.isEmpty(providedArgs)) { + for (Object providedArg : providedArgs) { + if (parameter.getParameterType().isInstance(providedArg)) { + return providedArg; + } + } + } + return null; + } + + protected static String formatArgumentError(MethodParameter param, String message) { + return "Could not resolve parameter [" + param.getParameterIndex() + "] in " + + param.getExecutable().toGenericString() + (StringUtils.hasText(message) ? ": " + message : ""); + } + + /** + * Assert that the target bean class is an instance of the class where the given + * method is declared. In some cases the actual controller instance at request- + * processing time may be a JDK dynamic proxy (lazy initialization, prototype + * beans, and others). {@code @Controller}'s that require proxying should prefer + * class-based proxy mechanisms. + */ + protected void assertTargetBean(Method method, Object targetBean, Object[] args) { + Class methodDeclaringClass = method.getDeclaringClass(); + Class targetBeanClass = targetBean.getClass(); + if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { + String text = "The mapped handler method class '" + methodDeclaringClass.getName() + + "' is not an instance of the actual controller bean class '" + + targetBeanClass.getName() + "'. If the controller requires proxying " + + "(e.g. due to @Transactional), please use class-based proxying."; + throw new IllegalStateException(formatInvokeError(text, args)); + } + } + + protected String formatInvokeError(String text, Object[] args) { + String formattedArgs = IntStream.range(0, args.length) + .mapToObj(i -> (args[i] != null ? + "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : + "[" + i + "] [null]")) + .collect(Collectors.joining(",\n", " ", " ")); + return text + "\n" + + "Controller [" + getBeanType().getName() + "]\n" + + "Method [" + getBridgedMethod().toGenericString() + "] " + + "with argument values:\n" + formattedArgs; + } + + /** * A MethodParameter with HandlerMethod-specific behavior. */ diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index d3e7c3679ff..524399b0b18 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -19,8 +19,6 @@ package org.springframework.web.method.support; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; @@ -28,7 +26,6 @@ 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; import org.springframework.web.bind.support.SessionStatus; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -182,23 +179,6 @@ public class InvocableHandlerMethod extends HandlerMethod { 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 : ""); - } - /** * Invoke the handler method with the given argument values. */ @@ -231,37 +211,4 @@ public class InvocableHandlerMethod extends HandlerMethod { } } - /** - * Assert that the target bean class is an instance of the class where the given - * method is declared. In some cases the actual controller instance at request- - * processing time may be a JDK dynamic proxy (lazy initialization, prototype - * beans, and others). {@code @Controller}'s that require proxying should prefer - * class-based proxy mechanisms. - */ - private void assertTargetBean(Method method, Object targetBean, Object[] args) { - Class methodDeclaringClass = method.getDeclaringClass(); - Class targetBeanClass = targetBean.getClass(); - if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { - String text = "The mapped handler method class '" + methodDeclaringClass.getName() + - "' is not an instance of the actual controller bean class '" + - targetBeanClass.getName() + "'. If the controller requires proxying " + - "(e.g. due to @Transactional), please use class-based proxying."; - throw new IllegalStateException(formatInvokeError(text, args)); - } - } - - private String formatInvokeError(String text, Object[] args) { - - String formattedArgs = IntStream.range(0, args.length) - .mapToObj(i -> (args[i] != null ? - "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : - "[" + i + "] [null]")) - .collect(Collectors.joining(",\n", " ", " ")); - - return text + "\n" + - "Controller [" + getBeanType().getName() + "]\n" + - "Method [" + getBridgedMethod().toGenericString() + "] " + - "with argument values:\n" + formattedArgs; - } - } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java index d9972c3e400..3a51a78cc0a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java @@ -22,8 +22,6 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import reactor.core.publisher.Mono; @@ -38,7 +36,6 @@ import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.HandlerResult; @@ -204,23 +201,6 @@ public class InvocableHandlerMethod extends HandlerMethod { Stream.of(values).map(o -> o != NO_ARG_VALUE ? o : null).toArray()); } - @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 : ""); - } - private void logArgumentErrorIfNecessary( ServerWebExchange exchange, MethodParameter parameter, Throwable cause) { @@ -233,39 +213,6 @@ public class InvocableHandlerMethod extends HandlerMethod { } } - /** - * Assert that the target bean class is an instance of the class where the given - * method is declared. In some cases the actual controller instance at request- - * processing time may be a JDK dynamic proxy (lazy initialization, prototype - * beans, and others). {@code @Controller}'s that require proxying should prefer - * class-based proxy mechanisms. - */ - private void assertTargetBean(Method method, Object targetBean, Object[] args) { - Class methodDeclaringClass = method.getDeclaringClass(); - Class targetBeanClass = targetBean.getClass(); - if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { - String text = "The mapped handler method class '" + methodDeclaringClass.getName() + - "' is not an instance of the actual controller bean class '" + - targetBeanClass.getName() + "'. If the controller requires proxying " + - "(e.g. due to @Transactional), please use class-based proxying."; - throw new IllegalStateException(formatInvokeError(text, args)); - } - } - - private String formatInvokeError(String text, Object[] args) { - - String formattedArgs = IntStream.range(0, args.length) - .mapToObj(i -> (args[i] != null ? - "[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" : - "[" + i + "] [null]")) - .collect(Collectors.joining(",\n", " ", " ")); - - return text + "\n" + - "Controller [" + getBeanType().getName() + "]\n" + - "Method [" + getBridgedMethod().toGenericString() + "]\n" + - "with argument values:\n" + formattedArgs; - } - private boolean isAsyncVoidReturnType(MethodParameter returnType, @Nullable ReactiveAdapter reactiveAdapter) {