diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java index 51fb1789..444d592f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java @@ -20,7 +20,6 @@ import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletionStage; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -31,13 +30,13 @@ import graphql.schema.GraphQLSchema; import graphql.schema.TypeResolver; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.TypeDefinitionRegistry; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import org.springframework.context.ApplicationContext; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.core.KotlinDetector; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.graphql.data.GraphQlArgumentBinder; import org.springframework.graphql.data.method.HandlerMethod; @@ -188,15 +187,16 @@ public final class FederationSchemaFactory public record EntityMappingInfo(String typeName, HandlerMethod handlerMethod) { public boolean isBatchHandlerMethod() { - MethodParameter type = handlerMethod().getReturnType(); - Class paramType = type.getParameterType(); - if (Flux.class.isAssignableFrom(paramType)) { - return true; + MethodParameter returnType = handlerMethod().getReturnType(); + Class clazz = returnType.getParameterType(); + ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(clazz); + if (adapter != null) { + if (adapter.isMultiValue()) { + return true; + } + returnType = returnType.nested(); } - if (Mono.class.isAssignableFrom(paramType) || CompletionStage.class.isAssignableFrom(paramType)) { - type = type.nested(); - } - return List.class.isAssignableFrom(type.getParameterType()); + return List.class.isAssignableFrom(returnType.getNestedParameterType()); } } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java index d5cbe0e8..4aeb0521 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java @@ -45,13 +45,6 @@ import graphql.schema.FieldCoordinates; import graphql.schema.GraphQLCodeRegistry; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.TypeDefinitionRegistry; -import kotlin.jvm.JvmClassMappingKt; -import kotlin.reflect.KFunction; -import kotlin.reflect.KType; -import kotlin.reflect.full.KClassifiers; -import kotlin.reflect.full.KTypes; -import kotlin.reflect.jvm.ReflectJvmMapping; -import kotlinx.coroutines.flow.Flow; import org.dataloader.DataLoader; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -64,6 +57,8 @@ import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.KotlinDetector; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.domain.ScrollPosition; @@ -384,15 +379,15 @@ public class AnnotatedControllerConfigurer clazz = returnType.getNestedParameterType(); } - if (clazz.equals(Flux.class) || Collection.class.isAssignableFrom(clazz) || - (KotlinDetector.isSuspendingFunction(method) && KotlinDelegate.isFlowReturnType(method))) { + ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(clazz); + if (Collection.class.isAssignableFrom(clazz) || (adapter != null && adapter.isMultiValue())) { registration.registerBatchLoader(invocable::invokeForIterable); ResolvableType valueType = ResolvableType.forMethodParameter(returnType.nested()); return new BatchMappingDataFetcher(info, valueType, dataLoaderKey); } - if (clazz.equals(Mono.class)) { + if (adapter != null) { returnType = returnType.nested(); clazz = returnType.getNestedParameterType(); } @@ -672,20 +667,4 @@ public class AnnotatedControllerConfigurer } } - - /** - * Inner class to avoid a hard dependency on Kotlin at runtime. - */ - private static final class KotlinDelegate { - - private static final KType flowType = - KClassifiers.getStarProjectedType(JvmClassMappingKt.getKotlinClass(Flow.class)); - - static boolean isFlowReturnType(Method method) { - KFunction function = ReflectJvmMapping.getKotlinFunction(method); - return (function != null && KTypes.isSubtypeOf(function.getReturnType(), flowType)); - } - - } - }