Polish SyncHandlerMethodArgumentResolver hiearchy
This commit is contained in:
@@ -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<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange);
|
||||
|
||||
@@ -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<HandlerMethodArgumentResolver> 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.
|
||||
|
||||
@@ -33,20 +33,25 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public interface SyncHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>By default this simply delegates to {@link #resolveArgumentValue} for
|
||||
* synchronous resolution.
|
||||
*/
|
||||
@Override
|
||||
default Mono<Object> resolveArgument(MethodParameter parameter, BindingContext context,
|
||||
default Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Optional<Object> 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<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange);
|
||||
|
||||
@@ -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<HandlerResult>}.
|
||||
*
|
||||
* @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<HandlerMethodArgumentResolver> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,9 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho
|
||||
protected abstract Mono<Object> 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<Object> getDefaultValue(NamedValueInfo namedValueInfo, MethodParameter parameter,
|
||||
BindingContext bindingContext, Model model, ServerWebExchange exchange) {
|
||||
|
||||
|
||||
@@ -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 Optional<Object> resolveArgumentValue(MethodParameter parameter,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
public Mono<Object> 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<Object> resolveArgumentValue(MethodParameter parameter,
|
||||
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<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
return Mono.justOrEmpty(resolveNamedValue(name, parameter, exchange));
|
||||
protected final Mono<Object> 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<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange);
|
||||
protected abstract Optional<Object> resolveNamedValue(String name,
|
||||
MethodParameter param, ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<SyncInvocableHandlerMethod> 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<String> 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<Object> 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(
|
||||
|
||||
@@ -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<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter methodParameter,
|
||||
BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
return Optional.of(bindingContext.getModel());
|
||||
return Optional.of(context.getModel());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter methodParameter,
|
||||
BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
Object value = exchange.getAttribute(name).orElse(Collections.emptyMap());
|
||||
|
||||
@@ -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<Object> resolveArgumentValue(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter methodParameter,
|
||||
BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
Class<?> paramType = methodParameter.getParameterType();
|
||||
boolean isMultiValueMap = MultiValueMap.class.isAssignableFrom(paramType);
|
||||
|
||||
HttpHeaders headers = exchange.getRequest().getHeaders();
|
||||
Object value = (isMultiValueMap(parameter) ? headers : headers.toSingleValueMap());
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
private boolean isMultiValueMap(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
return MultiValueMap.class.isAssignableFrom(paramType);
|
||||
return Optional.of(isMultiValueMap ? headers : headers.toSingleValueMap());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object> resolveArgumentValue(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter methodParameter,
|
||||
BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
MultiValueMap<String, String> 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<String, String> getRequestParams(ServerWebExchange exchange) {
|
||||
MultiValueMap<String, String> params = exchange.getRequestParams().subscribe().peek();
|
||||
Assert.notNull(params, "Expected form data (if any) to be parsed.");
|
||||
return params;
|
||||
}
|
||||
MultiValueMap<String, String> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object> resolveArgumentValue(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
public Optional<Object> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user