提交 f490f3ca 编写于 作者: R Rossen Stoyanchev

Polish SyncHandlerMethodArgumentResolver hiearchy

上级 dda8beeb
......@@ -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 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 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<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) {
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());
}
}
......@@ -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());
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册