From a8162c03f9dd0a2922204f11d93b82acc3efc989 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 29 Mar 2017 15:39:31 -0400 Subject: [PATCH] Add ArgumentResolverConfigurer Replace the List with a dedicated configurer that currently has one method accepting custom resolver registrations. --- .../DelegatingWebFluxConfiguration.java | 6 +-- .../config/WebFluxConfigurationSupport.java | 15 +++--- .../reactive/config/WebFluxConfigurer.java | 8 +-- .../config/WebFluxConfigurerComposite.java | 6 +-- .../ArgumentResolverConfigurer.java | 50 +++++++++++++++++ .../annotation/ControllerMethodResolver.java | 45 ++++++++-------- .../RequestMappingHandlerAdapter.java | 54 ++++++++++--------- .../DelegatingWebFluxConfigurationTests.java | 2 +- .../ControllerMethodResolverTests.java | 7 +-- 9 files changed, 123 insertions(+), 70 deletions(-) create mode 100644 spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ArgumentResolverConfigurer.java diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java index a986150401..d5addc01ed 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.java @@ -26,7 +26,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; -import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; /** * A subclass of {@code WebFluxConfigurationSupport} that detects and delegates @@ -70,8 +70,8 @@ public class DelegatingWebFluxConfiguration extends WebFluxConfigurationSupport } @Override - protected void addArgumentResolvers(List resolvers) { - this.configurers.addArgumentResolvers(resolvers); + protected void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { + this.configurers.configureArgumentResolvers(configurer); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java index cee40b2c91..4d923b5b16 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java @@ -16,7 +16,6 @@ package org.springframework.web.reactive.config; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,7 +49,7 @@ import org.springframework.web.reactive.accept.CompositeContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.handler.AbstractHandlerMapping; import org.springframework.web.reactive.result.SimpleHandlerAdapter; -import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; @@ -240,11 +239,9 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); adapter.setReactiveAdapterRegistry(webFluxAdapterRegistry()); - List resolvers = new ArrayList<>(); - addArgumentResolvers(resolvers); - if (!resolvers.isEmpty()) { - adapter.setCustomArgumentResolvers(resolvers); - } + ArgumentResolverConfigurer configurer = new ArgumentResolverConfigurer(); + configureArgumentResolvers(configurer); + adapter.setArgumentResolverConfigurer(configurer); return adapter; } @@ -257,9 +254,9 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { } /** - * Provide custom argument resolvers without overriding the built-in ones. + * Configure resolvers for custom controller method arguments. */ - protected void addArgumentResolvers(List resolvers) { + protected void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java index 1f51f69901..392e8431b7 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurer.java @@ -29,6 +29,7 @@ import org.springframework.web.reactive.accept.CompositeContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; /** * Defines callback methods to customize the configuration for Web Reactive @@ -80,11 +81,10 @@ public interface WebFluxConfigurer { } /** - * Provide custom controller method argument resolvers. Such resolvers do - * not override and will be invoked after the built-in ones. - * @param resolvers a list of resolvers to add + * Configure resolvers for custom controller method arguments. + * @param configurer to configurer to use */ - default void addArgumentResolvers(List resolvers) { + default void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java index bfb92ff1bb..b6a72713a0 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurerComposite.java @@ -28,7 +28,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; -import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; /** * A {@link WebFluxConfigurer} that delegates to one or more others. @@ -70,8 +70,8 @@ public class WebFluxConfigurerComposite implements WebFluxConfigurer { } @Override - public void addArgumentResolvers(List resolvers) { - this.delegates.forEach(delegate -> delegate.addArgumentResolvers(resolvers)); + public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { + this.delegates.forEach(delegate -> delegate.configureArgumentResolvers(configurer)); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ArgumentResolverConfigurer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ArgumentResolverConfigurer.java new file mode 100644 index 0000000000..d154291f63 --- /dev/null +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ArgumentResolverConfigurer.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.reactive.result.method.annotation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.util.Assert; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; + +/** + * Helps to configure resolvers for Controller method arguments. + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public class ArgumentResolverConfigurer { + + private final List customResolvers = new ArrayList<>(8); + + + /** + * Configure resolvers for custom controller method arguments. + * @param resolver the resolver(s) to add + */ + public void addCustomResolver(HandlerMethodArgumentResolver... resolver) { + Assert.notNull(resolver, "'resolvers' must not be null"); + this.customResolvers.addAll(Arrays.asList(resolver)); + } + + + List getCustomResolvers() { + return this.customResolvers; + } + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java index 13870f7b84..0af113cf4a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java @@ -17,6 +17,7 @@ package org.springframework.web.reactive.result.method.annotation; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -95,34 +96,36 @@ class ControllerMethodResolver { new LinkedHashMap<>(64); - ControllerMethodResolver(List customResolvers, + ControllerMethodResolver(ArgumentResolverConfigurer argumentResolverConfigurer, List> messageReaders, ReactiveAdapterRegistry reactiveRegistry, ConfigurableApplicationContext applicationContext) { - Assert.notNull(customResolvers, "'customResolvers' should not be null"); + Assert.notNull(argumentResolverConfigurer, "ArgumentResolverConfigurer is required"); Assert.notNull(reactiveRegistry, "ReactiveAdapterRegistry is required"); Assert.notNull(applicationContext, "ConfigurableApplicationContext is required"); - ResolverRegistrar registrar = ResolverRegistrar.customResolvers(customResolvers).basic(); + ArgumentResolverRegistrar registrar; + + registrar= ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).basic(); addResolversTo(registrar, reactiveRegistry, applicationContext); this.initBinderResolvers = registrar.getSyncResolvers(); - registrar = ResolverRegistrar.customResolvers(customResolvers).modelAttributeSupport(); + registrar = ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).modelAttributeSupport(); addResolversTo(registrar, reactiveRegistry, applicationContext); this.modelAttributeResolvers = registrar.getResolvers(); - registrar = ResolverRegistrar.customResolvers(customResolvers).fullSupport(messageReaders); + registrar = ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).fullSupport(messageReaders); addResolversTo(registrar, reactiveRegistry, applicationContext); this.requestMappingResolvers = registrar.getResolvers(); - registrar = ResolverRegistrar.customResolvers(customResolvers).basic(); + registrar = ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).basic(); addResolversTo(registrar, reactiveRegistry, applicationContext); this.exceptionHandlerResolvers = registrar.getResolvers(); initControllerAdviceCaches(applicationContext); } - private void addResolversTo(ResolverRegistrar registrar, + private void addResolversTo(ArgumentResolverRegistrar registrar, ReactiveAdapterRegistry reactiveRegistry, ConfigurableApplicationContext context) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); @@ -319,7 +322,7 @@ class ControllerMethodResolver { (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null); - private static class ResolverRegistrar { + private static class ArgumentResolverRegistrar { private final List customResolvers; @@ -330,10 +333,10 @@ class ControllerMethodResolver { private final List result = new ArrayList<>(); - private ResolverRegistrar(List customResolvers, + private ArgumentResolverRegistrar(ArgumentResolverConfigurer configurer, List> messageReaders, boolean modelAttribute) { - this.customResolvers = new ArrayList<>(customResolvers); + this.customResolvers = configurer.getCustomResolvers(); this.messageReaders = messageReaders != null ? new ArrayList<>(messageReaders) : null; this.modelAttributeSupported = modelAttribute; } @@ -372,32 +375,32 @@ class ControllerMethodResolver { } - public static Builder customResolvers(List customResolvers) { - return new Builder(customResolvers); + public static Builder configurer(ArgumentResolverConfigurer configurer) { + return new Builder(configurer); } public static class Builder { - private final List customResolvers; + private final ArgumentResolverConfigurer configurer; - public Builder(List customResolvers) { - this.customResolvers = new ArrayList<>(customResolvers); + public Builder(ArgumentResolverConfigurer configurer) { + this.configurer = configurer; } - public ResolverRegistrar fullSupport(List> readers) { + public ArgumentResolverRegistrar fullSupport(List> readers) { Assert.notEmpty(readers, "No message readers"); - return new ResolverRegistrar(this.customResolvers, readers, true); + return new ArgumentResolverRegistrar(this.configurer, readers, true); } - public ResolverRegistrar modelAttributeSupport() { - return new ResolverRegistrar(this.customResolvers, null, true); + public ArgumentResolverRegistrar modelAttributeSupport() { + return new ArgumentResolverRegistrar(this.configurer, null, true); } - public ResolverRegistrar basic() { - return new ResolverRegistrar(this.customResolvers, null, false); + public ArgumentResolverRegistrar basic() { + return new ArgumentResolverRegistrar(this.configurer, null, false); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 7b8a7efd2a..3a1fc17142 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -25,7 +25,6 @@ import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Mono; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; @@ -44,7 +43,6 @@ import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.HandlerAdapter; import org.springframework.web.reactive.HandlerResult; -import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; import org.springframework.web.reactive.result.method.InvocableHandlerMethod; import org.springframework.web.server.ServerWebExchange; @@ -63,9 +61,9 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application private WebBindingInitializer webBindingInitializer; - private ReactiveAdapterRegistry reactiveAdapterRegistry = new ReactiveAdapterRegistry(); + private ArgumentResolverConfigurer argumentResolverConfigurer; - private final List customArgumentResolvers = new ArrayList<>(8); + private ReactiveAdapterRegistry reactiveAdapterRegistry; private ConfigurableApplicationContext applicationContext; @@ -117,6 +115,21 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application return this.webBindingInitializer; } + /** + * Configure resolvers for controller method arguments. + */ + public void setArgumentResolverConfigurer(ArgumentResolverConfigurer configurer) { + Assert.notNull(configurer, "ArgumentResolverConfigurer is required"); + this.argumentResolverConfigurer = configurer; + } + + /** + * Return the configured resolvers for controller method arguments. + */ + public ArgumentResolverConfigurer getArgumentResolverConfigurer() { + return this.argumentResolverConfigurer; + } + /** * Configure the registry for adapting various reactive types. *

By default this is an instance of {@link ReactiveAdapterRegistry} with @@ -133,21 +146,6 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application return this.reactiveAdapterRegistry; } - /** - * Configure resolvers for custom controller method arguments. - */ - public void setCustomArgumentResolvers(List resolvers) { - this.customArgumentResolvers.clear(); - this.customArgumentResolvers.addAll(resolvers); - } - - /** - * Return the configured custom argument resolvers. - */ - public List getCustomArgumentResolvers() { - return this.customArgumentResolvers; - } - /** * A {@link ConfigurableApplicationContext} is expected for resolving * expressions in method argument default values as well as for @@ -164,18 +162,22 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application return this.applicationContext; } - public ConfigurableBeanFactory getBeanFactory() { - return this.applicationContext.getBeanFactory(); - } - @Override public void afterPropertiesSet() throws Exception { - this.methodResolver = new ControllerMethodResolver(getCustomArgumentResolvers(), - getMessageReaders(), getReactiveAdapterRegistry(), getApplicationContext()); + if (this.argumentResolverConfigurer == null) { + this.argumentResolverConfigurer = new ArgumentResolverConfigurer(); + } - this.modelInitializer = new ModelInitializer(getReactiveAdapterRegistry()); + if (this.reactiveAdapterRegistry == null) { + this.reactiveAdapterRegistry = new ReactiveAdapterRegistry(); + } + + this.methodResolver = new ControllerMethodResolver(this.argumentResolverConfigurer, + this.messageReaders, this.reactiveAdapterRegistry, this.applicationContext); + + this.modelInitializer = new ModelInitializer(this.reactiveAdapterRegistry); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java index b95cc46bfc..8b2ad176e8 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java @@ -100,7 +100,7 @@ public class DelegatingWebFluxConfigurationTests { verify(webFluxConfigurer).getValidator(); verify(webFluxConfigurer).getMessageCodesResolver(); verify(webFluxConfigurer).addFormatters(formatterRegistry.capture()); - verify(webFluxConfigurer).addArgumentResolvers(any()); + verify(webFluxConfigurer).configureArgumentResolvers(any()); assertSame(formatterRegistry.getValue(), initializerConversionService); assertEquals(9, codecsConfigurer.getValue().getReaders().size()); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolverTests.java index 88805790eb..410cd12584 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolverTests.java @@ -67,8 +67,9 @@ public class ControllerMethodResolverTests { @Before public void setUp() throws Exception { - List customResolvers = - Arrays.asList(new CustomArgumentResolver(), new CustomSyncArgumentResolver()); + ArgumentResolverConfigurer configurer = new ArgumentResolverConfigurer(); + configurer.addCustomResolver(new CustomArgumentResolver()); + configurer.addCustomResolver(new CustomSyncArgumentResolver()); List> messageReaders = Arrays.asList( new DecoderHttpMessageReader<>(new ByteArrayDecoder()), @@ -79,7 +80,7 @@ public class ControllerMethodResolverTests { applicationContext.refresh(); this.methodResolver = new ControllerMethodResolver( - customResolvers, messageReaders, new ReactiveAdapterRegistry(), applicationContext); + configurer, messageReaders, new ReactiveAdapterRegistry(), applicationContext); Method method = ResolvableMethod.on(TestController.class).mockCall(TestController::handle).method(); this.handlerMethod = new HandlerMethod(new TestController(), method);