From cd8c365a0d47d27a4117a24bff8fdf353073596e Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 29 Mar 2017 16:33:51 -0400 Subject: [PATCH] ServerCodecConfigurer as input in HandlerAdapter The ServerCodecConfigurer is now passed all the way into the RequestMappingHandlerAdapter which automatically enables the same defaults even without the Java config and provides extra flexibility in passing message codec configuration for the future. --- .../config/WebFluxConfigurationSupport.java | 2 +- .../annotation/ControllerMethodResolver.java | 52 +++++++++---------- .../RequestMappingHandlerAdapter.java | 41 +++++---------- .../WebFluxConfigurationSupportTests.java | 14 +++-- .../ControllerMethodResolverTests.java | 18 +++---- 5 files changed, 57 insertions(+), 70 deletions(-) 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 4d923b5b16..43f2e812f9 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 @@ -235,7 +235,7 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); - adapter.setMessageReaders(getMessageCodecsConfigurer().getReaders()); + adapter.setMessageCodecConfigurer(getMessageCodecsConfigurer()); adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); adapter.setReactiveAdapterRegistry(webFluxAdapterRegistry()); 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 0af113cf4a..8a7619925b 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,7 +17,6 @@ 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; @@ -38,6 +37,7 @@ import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.InitBinder; @@ -96,33 +96,34 @@ class ControllerMethodResolver { new LinkedHashMap<>(64); - ControllerMethodResolver(ArgumentResolverConfigurer argumentResolverConfigurer, - List> messageReaders, ReactiveAdapterRegistry reactiveRegistry, - ConfigurableApplicationContext applicationContext) { + ControllerMethodResolver(ArgumentResolverConfigurer argumentResolvers, + ServerCodecConfigurer messageCodecs, ReactiveAdapterRegistry reactiveRegistry, + ConfigurableApplicationContext context) { - Assert.notNull(argumentResolverConfigurer, "ArgumentResolverConfigurer is required"); + Assert.notNull(argumentResolvers, "ArgumentResolverConfigurer is required"); + Assert.notNull(messageCodecs, "ServerCodecConfigurer is required"); Assert.notNull(reactiveRegistry, "ReactiveAdapterRegistry is required"); - Assert.notNull(applicationContext, "ConfigurableApplicationContext is required"); + Assert.notNull(context, "ApplicationContext is required"); ArgumentResolverRegistrar registrar; - registrar= ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).basic(); - addResolversTo(registrar, reactiveRegistry, applicationContext); + registrar= ArgumentResolverRegistrar.configurer(argumentResolvers).basic(); + addResolversTo(registrar, reactiveRegistry, context); this.initBinderResolvers = registrar.getSyncResolvers(); - registrar = ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).modelAttributeSupport(); - addResolversTo(registrar, reactiveRegistry, applicationContext); + registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).modelAttributeSupport(); + addResolversTo(registrar, reactiveRegistry, context); this.modelAttributeResolvers = registrar.getResolvers(); - registrar = ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).fullSupport(messageReaders); - addResolversTo(registrar, reactiveRegistry, applicationContext); + registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).fullSupport(messageCodecs); + addResolversTo(registrar, reactiveRegistry, context); this.requestMappingResolvers = registrar.getResolvers(); - registrar = ArgumentResolverRegistrar.configurer(argumentResolverConfigurer).basic(); - addResolversTo(registrar, reactiveRegistry, applicationContext); + registrar = ArgumentResolverRegistrar.configurer(argumentResolvers).basic(); + addResolversTo(registrar, reactiveRegistry, context); this.exceptionHandlerResolvers = registrar.getResolvers(); - initControllerAdviceCaches(applicationContext); + initControllerAdviceCaches(context); } private void addResolversTo(ArgumentResolverRegistrar registrar, @@ -333,11 +334,11 @@ class ControllerMethodResolver { private final List result = new ArrayList<>(); - private ArgumentResolverRegistrar(ArgumentResolverConfigurer configurer, - List> messageReaders, boolean modelAttribute) { + private ArgumentResolverRegistrar(ArgumentResolverConfigurer resolvers, + ServerCodecConfigurer codecs, boolean modelAttribute) { - this.customResolvers = configurer.getCustomResolvers(); - this.messageReaders = messageReaders != null ? new ArrayList<>(messageReaders) : null; + this.customResolvers = resolvers.getCustomResolvers(); + this.messageReaders = codecs != null ? codecs.getReaders() : null; this.modelAttributeSupported = modelAttribute; } @@ -382,25 +383,24 @@ class ControllerMethodResolver { public static class Builder { - private final ArgumentResolverConfigurer configurer; + private final ArgumentResolverConfigurer resolvers; public Builder(ArgumentResolverConfigurer configurer) { - this.configurer = configurer; + this.resolvers = configurer; } - public ArgumentResolverRegistrar fullSupport(List> readers) { - Assert.notEmpty(readers, "No message readers"); - return new ArgumentResolverRegistrar(this.configurer, readers, true); + public ArgumentResolverRegistrar fullSupport(ServerCodecConfigurer codecs) { + return new ArgumentResolverRegistrar(this.resolvers, codecs, true); } public ArgumentResolverRegistrar modelAttributeSupport() { - return new ArgumentResolverRegistrar(this.configurer, null, true); + return new ArgumentResolverRegistrar(this.resolvers, null, true); } public ArgumentResolverRegistrar basic() { - return new ArgumentResolverRegistrar(this.configurer, null, false); + return new ArgumentResolverRegistrar(this.resolvers, 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 3a1fc17142..5bc91d9356 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 @@ -16,7 +16,6 @@ package org.springframework.web.reactive.result.method.annotation; -import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -29,13 +28,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.ReactiveAdapterRegistry; -import org.springframework.core.codec.ByteArrayDecoder; -import org.springframework.core.codec.ByteBufferDecoder; -import org.springframework.core.codec.DataBufferDecoder; -import org.springframework.core.codec.ResourceDecoder; -import org.springframework.core.codec.StringDecoder; -import org.springframework.http.codec.DecoderHttpMessageReader; -import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.util.Assert; import org.springframework.web.bind.support.WebBindingInitializer; @@ -57,7 +49,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application private static final Log logger = LogFactory.getLog(RequestMappingHandlerAdapter.class); - private final List> messageReaders = new ArrayList<>(32); + private ServerCodecConfigurer messageCodecConfigurer; private WebBindingInitializer webBindingInitializer; @@ -72,32 +64,19 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application private ModelInitializer modelInitializer; - public RequestMappingHandlerAdapter() { - this.messageReaders.add(new DecoderHttpMessageReader<>(new ByteArrayDecoder())); - this.messageReaders.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder())); - this.messageReaders.add(new DecoderHttpMessageReader<>(new DataBufferDecoder())); - this.messageReaders.add(new DecoderHttpMessageReader<>(new ResourceDecoder())); - this.messageReaders.add(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true))); - } - - /** * Configure HTTP message readers to de-serialize the request body with. - *

By default only basic data types such as bytes and text are registered. - * Consider using {@link ServerCodecConfigurer} to configure a richer list - * including JSON encoding . - * @see ServerCodecConfigurer + *

By default this is set to {@link ServerCodecConfigurer} with defaults. */ - public void setMessageReaders(List> messageReaders) { - this.messageReaders.clear(); - this.messageReaders.addAll(messageReaders); + public void setMessageCodecConfigurer(ServerCodecConfigurer configurer) { + this.messageCodecConfigurer = configurer; } /** - * Return the configured HTTP message readers. + * Return the configurer for HTTP message readers. */ - public List> getMessageReaders() { - return this.messageReaders; + public ServerCodecConfigurer getMessageCodecConfigurer() { + return this.messageCodecConfigurer; } /** @@ -166,6 +145,10 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application @Override public void afterPropertiesSet() throws Exception { + if (this.messageCodecConfigurer == null) { + this.messageCodecConfigurer = new ServerCodecConfigurer(); + } + if (this.argumentResolverConfigurer == null) { this.argumentResolverConfigurer = new ArgumentResolverConfigurer(); } @@ -175,7 +158,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application } this.methodResolver = new ControllerMethodResolver(this.argumentResolverConfigurer, - this.messageReaders, this.reactiveAdapterRegistry, this.applicationContext); + this.messageCodecConfigurer, this.reactiveAdapterRegistry, this.applicationContext); this.modelInitializer = new ModelInitializer(this.reactiveAdapterRegistry); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java index 17013136e7..1165957ade 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java @@ -68,8 +68,14 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.springframework.core.ResolvableType.*; -import static org.springframework.http.MediaType.*; +import static org.springframework.core.ResolvableType.forClass; +import static org.springframework.core.ResolvableType.forClassWithGenerics; +import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM; +import static org.springframework.http.MediaType.APPLICATION_XML; +import static org.springframework.http.MediaType.IMAGE_PNG; +import static org.springframework.http.MediaType.TEXT_PLAIN; /** * Unit tests for {@link WebFluxConfigurationSupport}. @@ -124,7 +130,7 @@ public class WebFluxConfigurationSupportTests { RequestMappingHandlerAdapter adapter = context.getBean(name, RequestMappingHandlerAdapter.class); assertNotNull(adapter); - List> readers = adapter.getMessageReaders(); + List> readers = adapter.getMessageCodecConfigurer().getReaders(); assertEquals(9, readers.size()); assertHasMessageReader(readers, forClass(byte[].class), APPLICATION_OCTET_STREAM); @@ -158,7 +164,7 @@ public class WebFluxConfigurationSupportTests { RequestMappingHandlerAdapter adapter = context.getBean(name, RequestMappingHandlerAdapter.class); assertNotNull(adapter); - List> messageReaders = adapter.getMessageReaders(); + List> messageReaders = adapter.getMessageCodecConfigurer().getReaders(); assertEquals(2, messageReaders.size()); assertHasMessageReader(messageReaders, forClass(String.class), TEXT_PLAIN); 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 410cd12584..006b3f6a51 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 @@ -17,7 +17,6 @@ package org.springframework.web.reactive.result.method.annotation; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; @@ -32,8 +31,7 @@ import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.codec.ByteArrayDecoder; import org.springframework.core.codec.ByteBufferDecoder; import org.springframework.http.HttpStatus; -import org.springframework.http.codec.DecoderHttpMessageReader; -import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -67,20 +65,20 @@ public class ControllerMethodResolverTests { @Before public void setUp() throws Exception { - ArgumentResolverConfigurer configurer = new ArgumentResolverConfigurer(); - configurer.addCustomResolver(new CustomArgumentResolver()); - configurer.addCustomResolver(new CustomSyncArgumentResolver()); + ArgumentResolverConfigurer resolvers = new ArgumentResolverConfigurer(); + resolvers.addCustomResolver(new CustomArgumentResolver()); + resolvers.addCustomResolver(new CustomSyncArgumentResolver()); - List> messageReaders = Arrays.asList( - new DecoderHttpMessageReader<>(new ByteArrayDecoder()), - new DecoderHttpMessageReader<>(new ByteBufferDecoder())); + ServerCodecConfigurer codecs = new ServerCodecConfigurer(); + codecs.customCodec().decoder(new ByteArrayDecoder()); + codecs.customCodec().decoder(new ByteBufferDecoder()); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.registerBean(TestControllerAdvice.class); applicationContext.refresh(); this.methodResolver = new ControllerMethodResolver( - configurer, messageReaders, new ReactiveAdapterRegistry(), applicationContext); + resolvers, codecs, new ReactiveAdapterRegistry(), applicationContext); Method method = ResolvableMethod.on(TestController.class).mockCall(TestController::handle).method(); this.handlerMethod = new HandlerMethod(new TestController(), method);