From e1a382b61f7f2c2d63434bd4575586acd8e881a8 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 1 Nov 2016 18:15:11 +0200 Subject: [PATCH] Polish --- .../result/method/InvocableHandlerMethod.java | 115 ++++++++++-------- .../method/SyncInvocableHandlerMethod.java | 23 +++- .../RequestMappingHandlerAdapter.java | 2 +- 3 files changed, 80 insertions(+), 60 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java index a8dacdbc8a..3ad4aaffb6 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -40,17 +41,17 @@ import org.springframework.web.reactive.HandlerResult; import org.springframework.web.server.ServerWebExchange; /** - * Extension of HandlerMethod that can invoke the target method after resolving - * its method arguments. + * A sub-class of {@link HandlerMethod} that can resolve method arguments from + * a {@link ServerWebExchange} and use that to invoke the underlying method. * * @author Rossen Stoyanchev * @since 5.0 */ public class InvocableHandlerMethod extends HandlerMethod { - private static final Mono NO_ARGS = Mono.just(new Object[0]); + private static final Mono EMPTY_ARGS = Mono.just(new Object[0]); - private static final Object NO_VALUE = new Object(); + private static final Object NO_ARG_VALUE = new Object(); private List resolvers = new ArrayList<>(); @@ -68,8 +69,8 @@ public class InvocableHandlerMethod extends HandlerMethod { /** - * Set {@link HandlerMethodArgumentResolver}s to use to use for resolving - * method argument values. + * Configure the argument resolvers to use to use for resolving method + * argument values against a {@code ServerWebExchange}. */ public void setArgumentResolvers(List resolvers) { this.resolvers.clear(); @@ -81,24 +82,18 @@ public class InvocableHandlerMethod extends HandlerMethod { * (e.g. default request attribute name). *

Default is a {@link DefaultParameterNameDiscoverer}. */ - public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { - this.parameterNameDiscoverer = parameterNameDiscoverer; - } - - @Override - protected Method getBridgedMethod() { - return super.getBridgedMethod(); + public void setParameterNameDiscoverer(ParameterNameDiscoverer nameDiscoverer) { + this.parameterNameDiscoverer = nameDiscoverer; } /** - * Invoke the method and return a Publisher for the return value. + * Invoke the method for the given exchange. + * * @param exchange the current exchange * @param bindingContext the binding context to use - * @param providedArgs optional list of argument values to check by type - * (via {@code instanceof}) for resolving method arguments. - * @return Publisher that produces a single HandlerResult or an error signal; - * never throws an exception + * @param providedArgs optional list of argument values to match by type + * @return Mono with a {@link HandlerResult}. */ public Mono invoke(ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) { @@ -124,59 +119,73 @@ public class InvocableHandlerMethod extends HandlerMethod { BindingContext bindingContext, Object... providedArgs) { if (ObjectUtils.isEmpty(getMethodParameters())) { - return NO_ARGS; + return EMPTY_ARGS; } try { - List> monos = Stream.of(getMethodParameters()) + List> argMonos = Stream.of(getMethodParameters()) .map(param -> { param.initParameterNameDiscovery(this.parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(param, getBean().getClass()); - if (!ObjectUtils.isEmpty(providedArgs)) { - for (Object providedArg : providedArgs) { - if (param.getParameterType().isInstance(providedArg)) { - return Mono.just(providedArg).log("reactor.resolved"); - } - } - } - HandlerMethodArgumentResolver resolver = this.resolvers.stream() - .filter(r -> r.supportsParameter(param)) - .findFirst() - .orElseThrow(() -> getArgError("No resolver for ", param, null)); - try { - return resolver.resolveArgument(param, bindingContext, exchange) - .defaultIfEmpty(NO_VALUE) - .doOnError(cause -> { - if(logger.isDebugEnabled()) { - logger.debug(getDetailedErrorMessage("Error resolving ", param), cause); - } - }) - .log("reactor.unresolved"); - } - catch (Exception ex) { - throw getArgError("Error resolving ", param, ex); - } + return findProvidedArg(param, providedArgs) + .map(Mono::just) + .orElseGet(() -> { + HandlerMethodArgumentResolver resolver = findResolver(param); + return resolveArg(resolver, param, bindingContext, exchange); + }); + }) .collect(Collectors.toList()); // Create Mono with array of resolved values... - return Mono.when(monos, - args -> Stream.of(args).map(o -> o != NO_VALUE ? o : null).toArray()); + return Mono.when(argMonos, argValues -> + Stream.of(argValues).map(o -> o != NO_ARG_VALUE ? o : null).toArray()); } catch (Throwable ex) { return Mono.error(ex); } } - private IllegalStateException getArgError(String message, MethodParameter param, Throwable cause) { - return new IllegalStateException(getDetailedErrorMessage(message, param), cause); + private Optional findProvidedArg(MethodParameter param, Object... providedArgs) { + if (ObjectUtils.isEmpty(providedArgs)) { + return Optional.empty(); + } + return Arrays.stream(providedArgs) + .filter(arg -> param.getParameterType().isInstance(arg)) + .findFirst(); + } + + private HandlerMethodArgumentResolver findResolver(MethodParameter param) { + return this.resolvers.stream() + .filter(r -> r.supportsParameter(param)) + .findFirst() + .orElseThrow(() -> getArgumentError("No resolver for ", param, null)); + } + + private Mono resolveArg(HandlerMethodArgumentResolver resolver, MethodParameter param, + BindingContext bindingContext, ServerWebExchange exchange) { + + try { + return resolver.resolveArgument(param, bindingContext, exchange) + .defaultIfEmpty(NO_ARG_VALUE) + .doOnError(cause -> { + if(logger.isDebugEnabled()) { + logger.debug(getDetailedErrorMessage("Error resolving ", param), cause); + } + }); + } + catch (Exception ex) { + throw getArgumentError("Error resolving ", param, ex); + } + } + + private IllegalStateException getArgumentError(String message, MethodParameter param, Throwable ex) { + return new IllegalStateException(getDetailedErrorMessage(message, param), ex); } private String getDetailedErrorMessage(String message, MethodParameter param) { - StringBuilder sb = new StringBuilder(message); - sb.append("argument [").append(param.getParameterIndex()).append("] "); - sb.append("of type [").append(param.getParameterType().getName()).append("] "); - sb.append("on method [").append(getBridgedMethod().toGenericString()).append("]"); - return sb.toString(); + return message + "argument [" + param.getParameterIndex() + "] " + + "of type [" + param.getParameterType().getName() + "] " + + "on method [" + getBridgedMethod().toGenericString() + "]"; } private Object doInvoke(Object[] args) throws Exception { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java index 8976ca1413..ef7a17d341 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java @@ -16,7 +16,7 @@ package org.springframework.web.reactive.result.method; import java.lang.reflect.Method; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import org.springframework.util.Assert; @@ -25,9 +25,10 @@ import org.springframework.web.reactive.HandlerResult; import org.springframework.web.server.ServerWebExchange; /** - * An extension of {@code InvocableHandlerMethod} for use with - * {@link SyncHandlerMethodArgumentResolver}s which in turn enables synchronous - * handler method invocation via {@link #invokeForHandlerResult}. + * An extension of {@code InvocableHandlerMethod} for synchronous, non-blocking + * method invocation via {@link #invokeForHandlerResult}. By allowing only + * {@link SyncHandlerMethodArgumentResolver}s to be configured, the invocation + * is guaranteed to be non-blocking. * * @author Rossen Stoyanchev * @since 5.0 @@ -45,8 +46,9 @@ public class SyncInvocableHandlerMethod extends InvocableHandlerMethod { /** - * Overloaded variant of the same setter from the base class that ensures - * all resolvers are {@link SyncHandlerMethodArgumentResolver}. + * {@inheritDoc} + *

Resolvers must be of type {@link SyncHandlerMethodArgumentResolver}. + * @see #setSyncArgumentResolvers(List) */ @Override public void setArgumentResolvers(List resolvers) { @@ -56,6 +58,15 @@ public class SyncInvocableHandlerMethod extends InvocableHandlerMethod { super.setArgumentResolvers(resolvers); } + /** + * Convenient alternative to {@link #setArgumentResolvers(List)} to configure + * synchronous argument resolvers. + */ + public void setSyncArgumentResolvers(List resolvers) { + setArgumentResolvers(new ArrayList<>(resolvers)); + } + + /** * Delegate to the base class {@link #invoke} and also wait for the result. * Since all argument resolvers are synchronous this won't actually block. diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 55e8ae83b9..7904e4432c 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -311,7 +311,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory for (Method method : methods) { Object bean = handlerMethod.getBean(); SyncInvocableHandlerMethod initBinderMethod = new SyncInvocableHandlerMethod(bean, method); - initBinderMethod.setArgumentResolvers(new ArrayList<>(this.initBinderArgumentResolvers)); + initBinderMethod.setSyncArgumentResolvers(getInitBinderArgumentResolvers()); initBinderMethods.add(initBinderMethod); } return new InitBinderBindingContext(getWebBindingInitializer(), initBinderMethods);