From f23784234aebefca0f98b399514426514d43afca Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 9 Mar 2023 13:04:29 +0000 Subject: [PATCH] Polishing --- ...andlerMethodArgumentResolverComposite.java | 23 ++-- .../AnnotatedControllerConfigurer.java | 73 +++++++------ ...BeanFactoryInitializationAotProcessor.java | 100 ++++++++++-------- ...tatedControllerExceptionResolverTests.java | 4 +- 4 files changed, 104 insertions(+), 96 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java index b6b7c7f5..dd7fd899 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/HandlerMethodArgumentResolverComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -27,9 +27,8 @@ import org.springframework.core.MethodParameter; import org.springframework.lang.Nullable; /** - * Resolves method parameters by delegating to a list of registered - * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolver}'s. - * Previously resolved method parameters are cached for faster lookups. + * Container for a list of resolvers that looks for one that supports a given + * method parameter type, and delegates to it. * * @author Rossen Stoyanchev * @since 1.0.0 @@ -63,7 +62,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu */ @Override public boolean supportsParameter(MethodParameter parameter) { - return getArgumentResolver(parameter) != null; + return (getArgumentResolver(parameter) != null); } /** @@ -77,8 +76,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception { HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { - throw new IllegalArgumentException("Unsupported parameter type [" + - parameter.getParameterType().getName() + "]. supportsParameter should be called first."); + throw new IllegalArgumentException("Unsupported parameter [" + parameter + "]."); } return resolver.resolveArgument(parameter, environment); } @@ -89,17 +87,14 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu */ @Nullable public HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { - HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); - if (result == null) { + return this.argumentResolverCache.computeIfAbsent(parameter, p -> { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { - result = resolver; - this.argumentResolverCache.put(parameter, result); - break; + return resolver; } } - } - return result; + return null; + }); } } \ No newline at end of file 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 21814613..53e807de 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 @@ -121,12 +121,6 @@ public class AnnotatedControllerConfigurer private final FormattingConversionService conversionService = new DefaultFormattingConversionService(); - @Nullable - private Executor executor; - - @Nullable - private ApplicationContext applicationContext; - @Nullable private HandlerMethodArgumentResolverComposite argumentResolvers; @@ -136,6 +130,12 @@ public class AnnotatedControllerConfigurer @Nullable private AnnotatedControllerExceptionResolver exceptionResolver; + @Nullable + private Executor executor; + + @Nullable + private ApplicationContext applicationContext; + /** * Add a {@code FormatterRegistrar} to customize the {@link ConversionService} @@ -147,31 +147,10 @@ public class AnnotatedControllerConfigurer registrar.registerFormatters(this.conversionService); } - /** - * Configure an {@link Executor} to use for asynchronous handling of - * {@link Callable} return values from controller methods. - *

By default, this is not set in which case controller methods with a - * {@code Callable} return value cannot be registered. - * @param executor the executor to use - */ - public void setExecutor(Executor executor) { - this.executor = executor; - } - - /** - * Configure an initializer that configures the {@link DataBinder} before the binding process. - * @param consumer the data binder initializer - * @since 1.0.1 - * @deprecated this property is deprecated, ignored, and should not be - * necessary as a {@link DataBinder} is no longer used to bind arguments - */ - @Deprecated(since = "1.1.0", forRemoval = true) - public void setDataBinderInitializer(@Nullable Consumer consumer) { - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; + HandlerMethodArgumentResolverComposite getArgumentResolvers() { + Assert.notNull(this.argumentResolvers, + "HandlerMethodArgumentResolverComposite is not yet initialized, was afterPropertiesSet called?"); + return this.argumentResolvers; } /** @@ -189,15 +168,39 @@ public class AnnotatedControllerConfigurer * @since 1.2 */ public DataFetcherExceptionResolver getExceptionResolver() { - Assert.notNull(this.exceptionResolver, "ExceptionResolver is not initialized, was afterPropertiesSet called?"); + Assert.notNull(this.exceptionResolver, + "DataFetcherExceptionResolver is not yet initialized, was afterPropertiesSet called?"); return (ex, env) -> this.exceptionResolver.resolveException(ex, env, null); } - @Nullable - HandlerMethodArgumentResolverComposite getArgumentResolvers() { - return this.argumentResolvers; + /** + * Configure an initializer that configures the {@link DataBinder} before the binding process. + * @param consumer the data binder initializer + * @since 1.0.1 + * @deprecated this property is deprecated, ignored, and should not be + * necessary as a {@link DataBinder} is no longer used to bind arguments + */ + @Deprecated(since = "1.1.0", forRemoval = true) + public void setDataBinderInitializer(@Nullable Consumer consumer) { } + /** + * Configure an {@link Executor} to use for asynchronous handling of + * {@link Callable} return values from controller methods. + *

By default, this is not set in which case controller methods with a + * {@code Callable} return value cannot be registered. + * @param executor the executor to use + */ + public void setExecutor(Executor executor) { + this.executor = executor; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override public void afterPropertiesSet() { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java index 8e18ccba..68e330c6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingBeanFactoryInitializationAotProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 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. @@ -47,7 +47,6 @@ import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComp import org.springframework.graphql.data.method.annotation.BatchMapping; import org.springframework.graphql.data.method.annotation.SchemaMapping; import org.springframework.stereotype.Controller; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -57,17 +56,21 @@ import static org.springframework.core.annotation.MergedAnnotations.SearchStrate * {@link BeanFactoryInitializationAotProcessor} implementation for registering * runtime hints discoverable through GraphQL controllers, such as: *

- *

This processor is using a {@link HandlerMethodArgumentResolver} resolution mechanism similar - * to the one used in {@link AnnotatedControllerConfigurer}. The type of runtime hints registered - * for each method argument depends on the {@link HandlerMethodArgumentResolver} resolved. - *

Manual registration of {@link graphql.schema.DataFetcher} cannot be detected by this - * processor; developers will need to declare bound types with {@link RegisterReflectionForBinding} - * annotations on their configuration class. + *

This processor is using a {@link HandlerMethodArgumentResolver} resolution + * mechanism similar to the one used in {@link AnnotatedControllerConfigurer}. + * The type of runtime hints registered for each method argument depends on the + * {@link HandlerMethodArgumentResolver} resolved. + *

Manual registration of {@link graphql.schema.DataFetcher} cannot be detected + * by this processor; developers will need to declare bound types with + * {@link RegisterReflectionForBinding} annotations on their configuration class. * * @author Brian Clozel * @see org.springframework.graphql.data.method.HandlerMethodArgumentResolver @@ -93,7 +96,9 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI return MergedAnnotations.from(element, TYPE_HIERARCHY).isPresent(Controller.class); } - private static class SchemaMappingBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution { + + private static class SchemaMappingBeanFactoryInitializationAotContribution + implements BeanFactoryInitializationAotContribution { private final Class[] controllers; @@ -105,21 +110,21 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } private HandlerMethodArgumentResolverComposite createArgumentResolvers() { - AnnotatedControllerConfigurer controllerConfigurer = new AnnotatedControllerConfigurer(); - controllerConfigurer.setApplicationContext(new StaticApplicationContext()); - controllerConfigurer.afterPropertiesSet(); - HandlerMethodArgumentResolverComposite argumentResolverComposite = controllerConfigurer.getArgumentResolvers(); - Assert.notNull(argumentResolverComposite, "argument resolvers should not be null"); - return argumentResolverComposite; + AnnotatedControllerConfigurer configurer = new AnnotatedControllerConfigurer(); + configurer.setApplicationContext(new StaticApplicationContext()); + configurer.afterPropertiesSet(); + return configurer.getArgumentResolvers(); } @Override - public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) { - RuntimeHints runtimeHints = generationContext.getRuntimeHints(); + public void applyTo(GenerationContext context, BeanFactoryInitializationCode initializationCode) { + RuntimeHints runtimeHints = context.getRuntimeHints(); registerSpringDataSpelSupport(runtimeHints); Arrays.stream(this.controllers).forEach(controller -> { runtimeHints.reflection().registerType(controller); - ReflectionUtils.doWithMethods(controller, method -> processSchemaMappingMethod(runtimeHints, method), this::isGraphQlHandlerMethod); + ReflectionUtils.doWithMethods(controller, + method -> processSchemaMappingMethod(runtimeHints, method), + this::isGraphQlHandlerMethod); }); } @@ -134,9 +139,8 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } private boolean isGraphQlHandlerMethod(AnnotatedElement element) { - MergedAnnotations mergedAnnotations = MergedAnnotations.from(element, TYPE_HIERARCHY); - return mergedAnnotations.isPresent(SchemaMapping.class) - || mergedAnnotations.isPresent(BatchMapping.class); + MergedAnnotations annotations = MergedAnnotations.from(element, TYPE_HIERARCHY); + return annotations.isPresent(SchemaMapping.class) || annotations.isPresent(BatchMapping.class); } private void processSchemaMappingMethod(RuntimeHints runtimeHints, Method method) { @@ -147,17 +151,17 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI processReturnType(runtimeHints, MethodParameter.forExecutable(method, -1)); } - private void processMethodParameter(RuntimeHints runtimeHints, MethodParameter methodParameter) { - MethodParameterRuntimeHintsRegistrar.fromMethodParameter(this.argumentResolvers, methodParameter) - .apply(runtimeHints); + private void processMethodParameter(RuntimeHints hints, MethodParameter parameter) { + MethodParameterRuntimeHintsRegistrar.fromMethodParameter(this.argumentResolvers, parameter).apply(hints); } - private void processReturnType(RuntimeHints runtimeHints, MethodParameter methodParameter) { - new ArgumentBindingHints(methodParameter).apply(runtimeHints); + private void processReturnType(RuntimeHints hints, MethodParameter parameter) { + new ArgumentBindingHints(parameter).apply(hints); } } + @FunctionalInterface private interface MethodParameterRuntimeHintsRegistrar { @@ -165,18 +169,20 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI void apply(RuntimeHints runtimeHints); - static MethodParameterRuntimeHintsRegistrar fromMethodParameter(HandlerMethodArgumentResolverComposite argumentResolvers, MethodParameter methodParameter) { - HandlerMethodArgumentResolver argumentResolver = argumentResolvers.getArgumentResolver(methodParameter); - if (argumentResolver instanceof ArgumentMethodArgumentResolver - || argumentResolver instanceof ArgumentsMethodArgumentResolver) { - return new ArgumentBindingHints(methodParameter); + static MethodParameterRuntimeHintsRegistrar fromMethodParameter( + HandlerMethodArgumentResolverComposite resolvers, MethodParameter parameter) { + + HandlerMethodArgumentResolver resolver = resolvers.getArgumentResolver(parameter); + if (resolver instanceof ArgumentMethodArgumentResolver + || resolver instanceof ArgumentsMethodArgumentResolver) { + return new ArgumentBindingHints(parameter); } - if (argumentResolver instanceof DataLoaderMethodArgumentResolver) { - return new DataLoaderHints(methodParameter); + if (resolver instanceof DataLoaderMethodArgumentResolver) { + return new DataLoaderHints(parameter); } if (springDataPresent) { - if (argumentResolver instanceof ProjectedPayloadMethodArgumentResolver) { - return new ProjectedPayloadHints(methodParameter); + if (resolver instanceof ProjectedPayloadMethodArgumentResolver) { + return new ProjectedPayloadHints(parameter); } } return new NoHintsRequired(); @@ -184,6 +190,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } + private static class NoHintsRequired implements MethodParameterRuntimeHintsRegistrar { @Override @@ -192,6 +199,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } } + private static class ArgumentBindingHints implements MethodParameterRuntimeHintsRegistrar { private final MethodParameter methodParameter; @@ -210,6 +218,7 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } } + private static class DataLoaderHints implements MethodParameterRuntimeHintsRegistrar { private final MethodParameter methodParameter; @@ -219,12 +228,13 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } @Override - public void apply(RuntimeHints runtimeHints) { - bindingRegistrar.registerReflectionHints(runtimeHints.reflection(), - this.methodParameter.nested().getNestedGenericParameterType()); + public void apply(RuntimeHints hints) { + bindingRegistrar.registerReflectionHints( + hints.reflection(), this.methodParameter.nested().getNestedGenericParameterType()); } } + private static class ProjectedPayloadHints implements MethodParameterRuntimeHintsRegistrar { private final MethodParameter methodParameter; @@ -234,10 +244,10 @@ class SchemaMappingBeanFactoryInitializationAotProcessor implements BeanFactoryI } @Override - public void apply(RuntimeHints runtimeHints) { + public void apply(RuntimeHints hints) { Class parameterType = this.methodParameter.nestedIfOptional().getNestedParameterType(); - runtimeHints.reflection().registerType(parameterType); - runtimeHints.proxies().registerJdkProxy(parameterType, TargetAware.class, SpringProxy.class, DecoratingProxy.class); + hints.reflection().registerType(parameterType); + hints.proxies().registerJdkProxy(parameterType, TargetAware.class, SpringProxy.class, DecoratingProxy.class); } } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolverTests.java index eeed59c9..910858e6 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolverTests.java @@ -148,8 +148,8 @@ public class AnnotatedControllerExceptionResolverTests { configurer.setApplicationContext(applicationContext); configurer.afterPropertiesSet(); - HandlerMethodArgumentResolverComposite argumentResolvers = configurer.getArgumentResolvers(); - AnnotatedControllerExceptionResolver resolver = new AnnotatedControllerExceptionResolver(argumentResolvers); + HandlerMethodArgumentResolverComposite resolvers = configurer.getArgumentResolvers(); + AnnotatedControllerExceptionResolver resolver = new AnnotatedControllerExceptionResolver(resolvers); resolver.registerControllerAdvice(applicationContext); return resolver; }