From f490f3ca63a56a6e31a271b513f89f2926b491fa Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 13 Mar 2017 12:29:08 -0400 Subject: [PATCH] Polish SyncHandlerMethodArgumentResolver hiearchy --- .../method/HandlerMethodArgumentResolver.java | 13 +++--- .../result/method/InvocableHandlerMethod.java | 15 ++++++- .../SyncHandlerMethodArgumentResolver.java | 15 ++++--- .../method/SyncInvocableHandlerMethod.java | 21 ++++++---- .../AbstractNamedValueArgumentResolver.java | 6 +++ ...bstractNamedValueSyncArgumentResolver.java | 40 ++++++++++++------- .../annotation/InitBinderBindingContext.java | 22 +++++----- .../annotation/ModelArgumentResolver.java | 7 ++-- ...PathVariableMapMethodArgumentResolver.java | 8 ++-- ...equestHeaderMapMethodArgumentResolver.java | 16 ++++---- ...RequestParamMapMethodArgumentResolver.java | 28 +++++-------- .../ServerWebExchangeArgumentResolver.java | 11 ++--- 12 files changed, 118 insertions(+), 84 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolver.java index 21215b0e26..fdf4446587 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolver.java @@ -23,23 +23,26 @@ import org.springframework.web.reactive.BindingContext; import org.springframework.web.server.ServerWebExchange; /** - * Strategy interface for resolving method parameters into argument values in - * the context of a given request. + * Strategy to resolve the argument value for a method parameter in the context + * of the current HTTP request. * * @author Rossen Stoyanchev * @since 5.0 */ public interface HandlerMethodArgumentResolver { + /** + * Whether this resolver supports the given method parameter. + * @param parameter the method parameter + */ boolean supportsParameter(MethodParameter parameter); /** - * The returned {@link Mono} may produce one or zero values if the argument - * does not resolve to any value, which will result in {@code null} passed - * as the argument value. + * Resolve the value for the method parameter. * @param parameter the method parameter * @param bindingContext the binding context to use * @param exchange the current exchange + * @return {@code Mono} for the argument value, possibly empty */ Mono resolveArgument(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange); 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 66dd274815..3052c5160c 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 @@ -29,7 +29,6 @@ import java.util.stream.Stream; import reactor.core.publisher.Mono; import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -94,6 +93,13 @@ public class InvocableHandlerMethod extends HandlerMethod { this.resolvers.addAll(resolvers); } + /** + * Return the conifgured argument resolvers. + */ + public List getResolvers() { + return this.resolvers; + } + /** * Set the ParameterNameDiscoverer for resolving parameter names when needed * (e.g. default request attribute name). @@ -103,6 +109,13 @@ public class InvocableHandlerMethod extends HandlerMethod { this.parameterNameDiscoverer = nameDiscoverer; } + /** + * Return the configured parameter name discoverer. + */ + public ParameterNameDiscoverer getParameterNameDiscoverer() { + return this.parameterNameDiscoverer; + } + /** * Invoke the method for the given exchange. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncHandlerMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncHandlerMethodArgumentResolver.java index b9601a6846..5652412842 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncHandlerMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncHandlerMethodArgumentResolver.java @@ -33,20 +33,25 @@ import org.springframework.web.server.ServerWebExchange; */ public interface SyncHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver { + + /** + * {@inheritDoc} + *

By default this simply delegates to {@link #resolveArgumentValue} for + * synchronous resolution. + */ @Override - default Mono resolveArgument(MethodParameter parameter, BindingContext context, + default Mono resolveArgument(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) { - Optional value = resolveArgumentValue(parameter, context, exchange); - return Mono.justOrEmpty(value); + return Mono.justOrEmpty(resolveArgumentValue(parameter, bindingContext, exchange)); } /** - * Resolve the method argument value synchronously returning an optional value. + * Resolve the value for the method parameter synchronously. * @param parameter the method parameter * @param bindingContext the binding context to use * @param exchange the current exchange - * @return the resolved value if any + * @return an {@code Optional} with the resolved value, possibly empty */ Optional resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java index cc020ddf17..c776cbe85c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/SyncInvocableHandlerMethod.java @@ -27,16 +27,17 @@ import org.springframework.web.reactive.HandlerResult; import org.springframework.web.server.ServerWebExchange; /** - * 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. + * Extension of {@code InvocableHandlerMethod} that can only be configured with + * synchronous argument resolvers and thus exposing an additional, synchronous + * {@link #invokeForHandlerResult} returning a {@code HandlerResult} vs + * {@code Mono}. * * @author Rossen Stoyanchev * @since 5.0 */ public class SyncInvocableHandlerMethod extends InvocableHandlerMethod { + public SyncInvocableHandlerMethod(HandlerMethod handlerMethod) { super(handlerMethod); } @@ -53,12 +54,16 @@ public class SyncInvocableHandlerMethod extends InvocableHandlerMethod { */ @Override public void setArgumentResolvers(List resolvers) { - resolvers.forEach(resolver -> - Assert.isInstanceOf(SyncHandlerMethodArgumentResolver.class, resolver, - "SyncHandlerMethodArgumentResolver requires SyncHandlerMethodArgumentResolver")); + resolvers.forEach(this::assertSyncResolvers); super.setArgumentResolvers(resolvers); } + private void assertSyncResolvers(HandlerMethodArgumentResolver resolver) { + Assert.isInstanceOf(SyncHandlerMethodArgumentResolver.class, resolver, + "SyncInvocableHandlerMethod requires resolvers of type " + + "SyncHandlerMethodArgumentResolver"); + } + /** * Convenient alternative to {@link #setArgumentResolvers(List)} to configure * synchronous argument resolvers. @@ -75,7 +80,7 @@ public class SyncInvocableHandlerMethod extends InvocableHandlerMethod { public HandlerResult invokeForHandlerResult(ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) { - // This will not block + // This will not block with only sync resolvers allowed return super.invoke(exchange, bindingContext, providedArgs).block(); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java index 0297537609..b9c1a7187e 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java @@ -171,6 +171,9 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho protected abstract Mono resolveName(String name, MethodParameter parameter, ServerWebExchange exchange); + /** + * Apply type conversion if necessary. + */ private Object applyConversion(Object value, NamedValueInfo namedValueInfo, MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) { @@ -187,6 +190,9 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho return value; } + /** + * Resolve the default value, if any. + */ private Mono getDefaultValue(NamedValueInfo namedValueInfo, MethodParameter parameter, BindingContext bindingContext, Model model, ServerWebExchange exchange) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java index 9b31533290..6cdefb1193 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java @@ -38,35 +38,45 @@ import org.springframework.web.server.ServerWebExchange; public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNamedValueArgumentResolver implements SyncHandlerMethodArgumentResolver { - public AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory beanFactory) { + + protected AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory beanFactory) { super(beanFactory); } + @Override + public Mono resolveArgument(MethodParameter parameter, BindingContext bindingContext, + ServerWebExchange exchange) { + + // Flip the default implementation from SyncHandlerMethodArgumentResolver: + // instead of delegating to (sync) resolveArgumentValue, + // call (async) super.resolveArgument shared with non-blocking resolvers; + // actual resolution below still sync... + + return super.resolveArgument(parameter, bindingContext, exchange); + } + @Override public Optional resolveArgumentValue(MethodParameter parameter, - BindingContext bindingContext, ServerWebExchange exchange) { + BindingContext context, ServerWebExchange exchange) { + + // This won't block since resolveName below doesn't + Object value = resolveArgument(parameter, context, exchange).block(); - // This will not block - Object value = resolveArgument(parameter, bindingContext, exchange).block(); return Optional.ofNullable(value); } @Override - protected Mono resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) { - return Mono.justOrEmpty(resolveNamedValue(name, parameter, exchange)); + protected final Mono resolveName(String name, MethodParameter param, + ServerWebExchange exchange) { + + return Mono.justOrEmpty(resolveNamedValue(name, param, exchange)); } /** - * An abstract method for synchronous resolution of method argument values - * that sub-classes must implement. - * @param name the name of the value being resolved - * @param parameter the method parameter to resolve to an argument value - * (pre-nested in case of a {@link java.util.Optional} declaration) - * @param exchange the current exchange - * @return the resolved argument value, if any + * Actually resolve the value synchronously. */ - protected abstract Optional resolveNamedValue(String name, MethodParameter parameter, - ServerWebExchange exchange); + protected abstract Optional resolveNamedValue(String name, + MethodParameter param, ServerWebExchange exchange); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java index aae9d30e98..be6b472bc9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContext.java @@ -29,8 +29,7 @@ import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod import org.springframework.web.server.ServerWebExchange; /** - * Variant of {@link BindingContext} that further initializes {@code DataBinder} - * instances through {@code @InitBinder} methods. + * Extends {@link BindingContext} with {@code @InitBinder} method initialization. * * @author Rossen Stoyanchev * @since 5.0 @@ -43,7 +42,7 @@ class InitBinderBindingContext extends BindingContext { private final BindingContext binderMethodContext; - public InitBinderBindingContext(WebBindingInitializer initializer, + InitBinderBindingContext(WebBindingInitializer initializer, List binderMethods) { super(initializer); @@ -53,24 +52,25 @@ class InitBinderBindingContext extends BindingContext { @Override - protected WebExchangeDataBinder initDataBinder(WebExchangeDataBinder binder, ServerWebExchange exchange) { + protected WebExchangeDataBinder initDataBinder(WebExchangeDataBinder dataBinder, + ServerWebExchange exchange) { this.binderMethods.stream() .filter(binderMethod -> { InitBinder annotation = binderMethod.getMethodAnnotation(InitBinder.class); Collection names = Arrays.asList(annotation.value()); - return (names.size() == 0 || names.contains(binder.getObjectName())); + return (names.size() == 0 || names.contains(dataBinder.getObjectName())); }) - .forEach(method -> invokeBinderMethod(binder, exchange, method)); + .forEach(method -> invokeBinderMethod(dataBinder, exchange, method)); - return binder; + return dataBinder; } - private void invokeBinderMethod(WebExchangeDataBinder binder, ServerWebExchange exchange, - SyncInvocableHandlerMethod binderMethod) { + private void invokeBinderMethod(WebExchangeDataBinder dataBinder, + ServerWebExchange exchange, SyncInvocableHandlerMethod binderMethod) { Optional returnValue = binderMethod - .invokeForHandlerResult(exchange, this.binderMethodContext, binder) + .invokeForHandlerResult(exchange, this.binderMethodContext, dataBinder) .getReturnValue(); if (returnValue.isPresent()) { @@ -78,7 +78,7 @@ class InitBinderBindingContext extends BindingContext { "@InitBinder methods should return void: " + binderMethod); } - // Should not happen (no argument resolvers)... + // Should not happen (no Model argument resolution) ... if (!this.binderMethodContext.getModel().asMap().isEmpty()) { throw new IllegalStateException( diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java index fcf97df360..99689f5bbc 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java @@ -32,16 +32,17 @@ import org.springframework.web.server.ServerWebExchange; */ public class ModelArgumentResolver implements SyncHandlerMethodArgumentResolver { + @Override public boolean supportsParameter(MethodParameter parameter) { return Model.class.isAssignableFrom(parameter.getParameterType()); } @Override - public Optional resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext, - ServerWebExchange exchange) { + public Optional resolveArgumentValue(MethodParameter methodParameter, + BindingContext context, ServerWebExchange exchange) { - return Optional.of(bindingContext.getModel()); + return Optional.of(context.getModel()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java index 0541b0ef78..c5e2adca1b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java @@ -40,6 +40,7 @@ import org.springframework.web.server.ServerWebExchange; */ public class PathVariableMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { + @Override public boolean supportsParameter(MethodParameter parameter) { PathVariable ann = parameter.getParameterAnnotation(PathVariable.class); @@ -47,12 +48,9 @@ public class PathVariableMapMethodArgumentResolver implements SyncHandlerMethodA && !StringUtils.hasText(ann.value())); } - /** - * Return a Map with all URI template variables or an empty map. - */ @Override - public Optional resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext, - ServerWebExchange exchange) { + public Optional resolveArgumentValue(MethodParameter methodParameter, + BindingContext context, ServerWebExchange exchange) { String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; Object value = exchange.getAttribute(name).orElse(Collections.emptyMap()); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java index 5e9a555917..33cf22e930 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java @@ -42,6 +42,7 @@ import org.springframework.web.server.ServerWebExchange; */ public class RequestHeaderMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { + @Override public boolean supportsParameter(MethodParameter parameter) { return (parameter.hasParameterAnnotation(RequestHeader.class) && @@ -49,17 +50,14 @@ public class RequestHeaderMapMethodArgumentResolver implements SyncHandlerMethod } @Override - public Optional resolveArgumentValue(MethodParameter parameter, BindingContext context, - ServerWebExchange exchange) { + public Optional resolveArgumentValue(MethodParameter methodParameter, + BindingContext context, ServerWebExchange exchange) { - HttpHeaders headers = exchange.getRequest().getHeaders(); - Object value = (isMultiValueMap(parameter) ? headers : headers.toSingleValueMap()); - return Optional.of(value); - } + Class paramType = methodParameter.getParameterType(); + boolean isMultiValueMap = MultiValueMap.class.isAssignableFrom(paramType); - private boolean isMultiValueMap(MethodParameter parameter) { - Class paramType = parameter.getParameterType(); - return MultiValueMap.class.isAssignableFrom(paramType); + HttpHeaders headers = exchange.getRequest().getHeaders(); + return Optional.of(isMultiValueMap ? headers : headers.toSingleValueMap()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java index 9461446cda..0bbafa80a0 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java @@ -45,11 +45,12 @@ import org.springframework.web.server.ServerWebExchange; */ public class RequestParamMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { + @Override - public boolean supportsParameter(MethodParameter parameter) { - RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); + public boolean supportsParameter(MethodParameter methodParam) { + RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class); if (requestParam != null) { - if (Map.class.isAssignableFrom(parameter.getParameterType())) { + if (Map.class.isAssignableFrom(methodParam.getParameterType())) { return !StringUtils.hasText(requestParam.name()); } } @@ -57,23 +58,16 @@ public class RequestParamMapMethodArgumentResolver implements SyncHandlerMethodA } @Override - public Optional resolveArgumentValue(MethodParameter parameter, BindingContext context, - ServerWebExchange exchange) { + public Optional resolveArgumentValue(MethodParameter methodParameter, + BindingContext context, ServerWebExchange exchange) { - MultiValueMap requestParams = getRequestParams(exchange); - Object value = (isMultiValueMap(parameter) ? requestParams : requestParams.toSingleValueMap()); - return Optional.of(value); - } + Class paramType = methodParameter.getParameterType(); + boolean isMultiValueMap = MultiValueMap.class.isAssignableFrom(paramType); - private MultiValueMap getRequestParams(ServerWebExchange exchange) { - MultiValueMap params = exchange.getRequestParams().subscribe().peek(); - Assert.notNull(params, "Expected form data (if any) to be parsed."); - return params; - } + MultiValueMap requestParams = exchange.getRequestParams().subscribe().peek(); + Assert.notNull(requestParams, "Expected form data (if any) to be parsed."); - private boolean isMultiValueMap(MethodParameter parameter) { - Class paramType = parameter.getParameterType(); - return MultiValueMap.class.isAssignableFrom(paramType); + return Optional.of(isMultiValueMap ? requestParams : requestParams.toSingleValueMap()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java index 07b77c5f11..342af7d236 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java @@ -45,6 +45,7 @@ import org.springframework.web.server.ServerWebExchange; */ public class ServerWebExchangeArgumentResolver implements SyncHandlerMethodArgumentResolver { + @Override public boolean supportsParameter(MethodParameter parameter) { Class paramType = parameter.getParameterType(); @@ -55,10 +56,10 @@ public class ServerWebExchangeArgumentResolver implements SyncHandlerMethodArgum } @Override - public Optional resolveArgumentValue(MethodParameter parameter, BindingContext context, - ServerWebExchange exchange) { + public Optional resolveArgumentValue(MethodParameter methodParameter, + BindingContext context, ServerWebExchange exchange) { - Class paramType = parameter.getParameterType(); + Class paramType = methodParameter.getParameterType(); if (ServerWebExchange.class.isAssignableFrom(paramType)) { return Optional.of(exchange); } @@ -73,8 +74,8 @@ public class ServerWebExchangeArgumentResolver implements SyncHandlerMethodArgum } else { // should never happen... - throw new IllegalArgumentException( - "Unknown parameter type: " + paramType + " in method: " + parameter.getMethod()); + throw new IllegalArgumentException("Unknown parameter type: " + + paramType + " in method: " + methodParameter.getMethod()); } }