Merge branch '6.1.x'
This commit is contained in:
@@ -36,6 +36,7 @@ import kotlin.reflect.full.KClasses;
|
||||
import kotlin.reflect.jvm.KCallablesJvm;
|
||||
import kotlin.reflect.jvm.ReflectJvmMapping;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
|
||||
import org.springframework.core.CoroutinesUtils;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
@@ -60,6 +61,12 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* Extension of {@link HandlerMethod} that invokes the underlying method with
|
||||
* argument values resolved from the current HTTP request through a list of
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
* <p>By default, the method invocation happens on the thread from which the
|
||||
* {@code Mono} was subscribed to, or in some cases the thread that emitted one
|
||||
* of the resolved arguments (e.g. when the request body needs to be decoded).
|
||||
* To ensure a predictable thread for the underlying method's invocation,
|
||||
* a {@link Scheduler} can optionally be provided via
|
||||
* {@link #setInvocationScheduler(Scheduler)}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
@@ -86,6 +93,9 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
|
||||
private Class<?>[] validationGroups = EMPTY_GROUPS;
|
||||
|
||||
@Nullable
|
||||
private Scheduler invocationScheduler;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance from a {@code HandlerMethod}.
|
||||
@@ -154,6 +164,13 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
methodValidator.determineValidationGroups(getBean(), getBridgedMethod()) : EMPTY_GROUPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Scheduler} on which to perform the method invocation.
|
||||
* @since 6.1.6
|
||||
*/
|
||||
public void setInvocationScheduler(@Nullable Scheduler invocationScheduler) {
|
||||
this.invocationScheduler = invocationScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method for the given exchange.
|
||||
@@ -166,7 +183,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
public Mono<HandlerResult> invoke(
|
||||
ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) {
|
||||
|
||||
return getMethodArgumentValues(exchange, bindingContext, providedArgs).flatMap(args -> {
|
||||
return getMethodArgumentValuesOnScheduler(exchange, bindingContext, providedArgs).flatMap(args -> {
|
||||
if (shouldValidateArguments() && this.methodValidator != null) {
|
||||
this.methodValidator.applyArgumentValidation(
|
||||
getBean(), getBridgedMethod(), getMethodParameters(), args, this.validationGroups);
|
||||
@@ -218,6 +235,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Object[]> getMethodArgumentValuesOnScheduler(
|
||||
ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) {
|
||||
Mono<Object[]> argumentValuesMono = getMethodArgumentValues(exchange, bindingContext, providedArgs);
|
||||
return this.invocationScheduler != null ? argumentValuesMono.publishOn(this.invocationScheduler) : argumentValuesMono;
|
||||
}
|
||||
|
||||
private Mono<Object[]> getMethodArgumentValues(
|
||||
ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -103,6 +104,12 @@ class ControllerMethodResolver {
|
||||
|
||||
private final ReactiveAdapterRegistry reactiveAdapterRegistry;
|
||||
|
||||
@Nullable
|
||||
private final Scheduler invocationScheduler;
|
||||
|
||||
@Nullable
|
||||
private final Predicate<? super HandlerMethod> blockingMethodPredicate;
|
||||
|
||||
@Nullable
|
||||
private final MethodValidator methodValidator;
|
||||
|
||||
@@ -125,7 +132,9 @@ class ControllerMethodResolver {
|
||||
ControllerMethodResolver(
|
||||
ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry adapterRegistry,
|
||||
ConfigurableApplicationContext context, List<HttpMessageReader<?>> readers,
|
||||
@Nullable WebBindingInitializer webBindingInitializer) {
|
||||
@Nullable WebBindingInitializer webBindingInitializer,
|
||||
@Nullable Scheduler invocationScheduler,
|
||||
@Nullable Predicate<? super HandlerMethod> blockingMethodPredicate) {
|
||||
|
||||
Assert.notNull(customResolvers, "ArgumentResolverConfigurer is required");
|
||||
Assert.notNull(adapterRegistry, "ReactiveAdapterRegistry is required");
|
||||
@@ -137,6 +146,8 @@ class ControllerMethodResolver {
|
||||
this.requestMappingResolvers = requestMappingResolvers(customResolvers, adapterRegistry, context, readers);
|
||||
this.exceptionHandlerResolvers = exceptionHandlerResolvers(customResolvers, adapterRegistry, context);
|
||||
this.reactiveAdapterRegistry = adapterRegistry;
|
||||
this.invocationScheduler = invocationScheduler;
|
||||
this.blockingMethodPredicate = blockingMethodPredicate;
|
||||
|
||||
if (BEAN_VALIDATION_PRESENT) {
|
||||
this.methodValidator = HandlerMethodValidator.from(webBindingInitializer, null,
|
||||
@@ -287,6 +298,21 @@ class ControllerMethodResolver {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Scheduler} for the given method if it is considered
|
||||
* blocking by the underlying blocking method predicate, or null if no
|
||||
* particular scheduler should be used for this method invocation.
|
||||
*/
|
||||
@Nullable
|
||||
public Scheduler getSchedulerFor(HandlerMethod handlerMethod) {
|
||||
if (this.invocationScheduler != null) {
|
||||
Assert.state(this.blockingMethodPredicate != null, "Expected HandlerMethod Predicate");
|
||||
if (this.blockingMethodPredicate.test(handlerMethod)) {
|
||||
return this.invocationScheduler;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link InvocableHandlerMethod} for the given
|
||||
@@ -297,6 +323,7 @@ class ControllerMethodResolver {
|
||||
invocable.setArgumentResolvers(this.requestMappingResolvers);
|
||||
invocable.setReactiveAdapterRegistry(this.reactiveAdapterRegistry);
|
||||
invocable.setMethodValidator(this.methodValidator);
|
||||
invocable.setInvocationScheduler(getSchedulerFor(handlerMethod));
|
||||
return invocable;
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +225,8 @@ public class RequestMappingHandlerAdapter
|
||||
|
||||
this.methodResolver = new ControllerMethodResolver(
|
||||
this.argumentResolverConfigurer, this.reactiveAdapterRegistry, this.applicationContext,
|
||||
this.messageReaders, this.webBindingInitializer);
|
||||
this.messageReaders, this.webBindingInitializer,
|
||||
this.scheduler, this.blockingMethodPredicate);
|
||||
|
||||
this.modelInitializer = new ModelInitializer(this.methodResolver, this.reactiveAdapterRegistry);
|
||||
}
|
||||
@@ -260,11 +261,9 @@ public class RequestMappingHandlerAdapter
|
||||
.doOnNext(result -> result.setExceptionHandler(exceptionHandler))
|
||||
.onErrorResume(ex -> exceptionHandler.handleError(exchange, ex));
|
||||
|
||||
if (this.scheduler != null) {
|
||||
Assert.state(this.blockingMethodPredicate != null, "Expected HandlerMethod Predicate");
|
||||
if (this.blockingMethodPredicate.test(handlerMethod)) {
|
||||
resultMono = resultMono.subscribeOn(this.scheduler);
|
||||
}
|
||||
Scheduler optionalScheduler = this.methodResolver.getSchedulerFor(handlerMethod);
|
||||
if (optionalScheduler != null) {
|
||||
return resultMono.subscribeOn(optionalScheduler);
|
||||
}
|
||||
|
||||
return resultMono;
|
||||
|
||||
Reference in New Issue
Block a user