提交 a48c9b63 编写于 作者: R Rossen Stoyanchev

Replace DefaultConversionService in spring-reactive

上级 c0dff3d2
/*
* Copyright 2002-2015 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.reactive.convert.support;
import reactor.core.publisher.convert.DependencyUtils;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToReactorConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
/**
* TODO temporary class designed to be replaced by org.springframework.core.convert.support.DefaultConversionService when it will contain Reactive Streams converter
* @author Sebastien Deleuze
*/
public class DefaultConversionService extends GenericConversionService {
public DefaultConversionService() {
addDefaultConverters(this);
}
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverter(new ReactiveStreamsToCompletableFutureConverter());
if (DependencyUtils.hasReactorStream()) {
converterRegistry.addConverter(new ReactiveStreamsToReactorConverter());
}
if (DependencyUtils.hasRxJava1()) {
converterRegistry.addConverter(new ReactiveStreamsToRxJava1Converter());
}
}
}
......@@ -19,6 +19,7 @@ package org.springframework.reactive.web.dispatch.method.annotation;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
......@@ -32,7 +33,9 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.reactive.codec.decoder.ByteToMessageDecoder;
import org.springframework.reactive.codec.decoder.JsonObjectDecoder;
import org.springframework.reactive.web.dispatch.method.HandlerMethodArgumentResolver;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;
/**
......@@ -44,25 +47,19 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final List<ByteToMessageDecoder<?>> deserializers;
private final List<ByteToMessageDecoder<ByteBuffer>> preProcessors;
private final List<ByteToMessageDecoder<?>> decoders;
private final ConversionService conversionService;
// TODO: remove field
private final List<ByteToMessageDecoder<ByteBuffer>> preProcessors = Arrays.asList(new JsonObjectDecoder());
public RequestBodyArgumentResolver(List<ByteToMessageDecoder<?>> decoders,
ConversionService conversionService) {
this(decoders, conversionService, Collections.EMPTY_LIST);
}
public RequestBodyArgumentResolver(List<ByteToMessageDecoder<?>> decoders,
ConversionService service, List<ByteToMessageDecoder<ByteBuffer>> preProcessors) {
this.deserializers = decoders;
public RequestBodyArgumentResolver(List<ByteToMessageDecoder<?>> decoders, ConversionService service) {
Assert.notEmpty(decoders, "At least one decoder is required.");
Assert.notNull(service, "'conversionService' is required.");
this.decoders = decoders;
this.conversionService = service;
this.preProcessors = preProcessors;
}
......@@ -80,10 +77,10 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
Publisher<ByteBuffer> inputStream = request.getBody();
Publisher<?> elementStream = inputStream;
ResolvableType elementType = type.hasGenerics() ? type.getGeneric(0) : type;
ByteToMessageDecoder<?> decoder = resolveDecoder(request, elementType, mediaType, hints.toArray());
ByteToMessageDecoder<?> decoder = resolveDecoder(elementType, mediaType, hints.toArray());
if (decoder != null) {
List<ByteToMessageDecoder<ByteBuffer>> preProcessors = resolvePreProcessors(
request, elementType, mediaType,hints.toArray());
elementType, mediaType,hints.toArray());
for (ByteToMessageDecoder<ByteBuffer> preProcessor : preProcessors) {
inputStream = preProcessor.decode(inputStream, elementType, mediaType, hints.toArray());
......@@ -103,10 +100,8 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
return ( mediaTypes.size() > 0 ? mediaTypes.get(0) : MediaType.TEXT_PLAIN);
}
private ByteToMessageDecoder<?> resolveDecoder(ReactiveServerHttpRequest request,
ResolvableType type, MediaType mediaType, Object[] hints) {
for (ByteToMessageDecoder<?> deserializer : this.deserializers) {
private ByteToMessageDecoder<?> resolveDecoder(ResolvableType type, MediaType mediaType, Object[] hints) {
for (ByteToMessageDecoder<?> deserializer : this.decoders) {
if (deserializer.canDecode(type, mediaType, hints)) {
return deserializer;
}
......@@ -114,9 +109,8 @@ public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolve
return null;
}
private List<ByteToMessageDecoder<ByteBuffer>> resolvePreProcessors(
ReactiveServerHttpRequest request, ResolvableType type, MediaType mediaType,
Object[] hints) {
private List<ByteToMessageDecoder<ByteBuffer>> resolvePreProcessors(ResolvableType type,
MediaType mediaType, Object[] hints) {
List<ByteToMessageDecoder<ByteBuffer>> preProcessors = new ArrayList<>();
for (ByteToMessageDecoder<ByteBuffer> preProcessor : this.preProcessors) {
......
......@@ -16,24 +16,21 @@
package org.springframework.reactive.web.dispatch.method.annotation;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.reactivestreams.Publisher;
import reactor.Publishers;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.http.server.ReactiveServerHttpResponse;
import org.springframework.reactive.codec.decoder.ByteBufferDecoder;
import org.springframework.reactive.codec.decoder.ByteToMessageDecoder;
import org.springframework.reactive.codec.decoder.JacksonJsonDecoder;
import org.springframework.reactive.codec.decoder.JsonObjectDecoder;
import org.springframework.reactive.codec.decoder.StringDecoder;
import org.springframework.reactive.convert.support.DefaultConversionService;
import org.springframework.reactive.web.dispatch.HandlerAdapter;
import org.springframework.reactive.web.dispatch.HandlerResult;
import org.springframework.reactive.web.dispatch.method.HandlerMethodArgumentResolver;
......@@ -48,12 +45,26 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
private List<HandlerMethodArgumentResolver> argumentResolvers;
private ConversionService conversionService;
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
this.argumentResolvers.clear();
this.argumentResolvers.addAll(resolvers);
}
public List<HandlerMethodArgumentResolver> getArgumentResolvers() {
return this.argumentResolvers;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public ConversionService getConversionService() {
return this.conversionService;
}
@Override
public void afterPropertiesSet() throws Exception {
......@@ -62,13 +73,9 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
List<ByteToMessageDecoder<?>> decoders = Arrays.asList(new ByteBufferDecoder(),
new StringDecoder(), new JacksonJsonDecoder());
List<ByteToMessageDecoder<ByteBuffer>> preProcessors = Collections.singletonList(
new JsonObjectDecoder());
this.argumentResolvers = new ArrayList<>();
this.argumentResolvers.add(new RequestParamArgumentResolver());
this.argumentResolvers.add(new RequestBodyArgumentResolver(decoders,
new DefaultConversionService(), preProcessors));
this.argumentResolvers.add(new RequestBodyArgumentResolver(decoders, this.conversionService));
}
}
......
......@@ -20,7 +20,7 @@ import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import org.reactivestreams.Publisher;
......@@ -35,10 +35,11 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ReactiveServerHttpRequest;
import org.springframework.http.server.ReactiveServerHttpResponse;
import org.springframework.reactive.codec.encoder.JsonObjectEncoder;
import org.springframework.reactive.codec.encoder.MessageToByteEncoder;
import org.springframework.reactive.convert.support.DefaultConversionService;
import org.springframework.reactive.web.dispatch.HandlerResult;
import org.springframework.reactive.web.dispatch.HandlerResultHandler;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
......@@ -56,29 +57,21 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
private final List<MessageToByteEncoder<?>> serializers;
private final List<MessageToByteEncoder<ByteBuffer>> postProcessors;
private final ConversionService conversionService;
private int order = 0;
// TODO: remove field
private final List<MessageToByteEncoder<ByteBuffer>> postProcessors = Arrays.asList(new JsonObjectEncoder());
public ResponseBodyResultHandler(List<MessageToByteEncoder<?>> encoders) {
this(encoders, Collections.EMPTY_LIST);
}
public ResponseBodyResultHandler(List<MessageToByteEncoder<?>> encoders,
List<MessageToByteEncoder<ByteBuffer>> postProcessors) {
this(encoders, postProcessors, new DefaultConversionService());
}
public ResponseBodyResultHandler(List<MessageToByteEncoder<?>> encoders,
List<MessageToByteEncoder<ByteBuffer>> postProcessors,
ConversionService conversionService) {
public ResponseBodyResultHandler(List<MessageToByteEncoder<?>> encoders, ConversionService service) {
Assert.notEmpty(encoders, "At least one encoder is required.");
Assert.notNull(service, "'conversionService' is required.");
this.serializers = encoders;
this.postProcessors = postProcessors;
this.conversionService = conversionService;
this.conversionService = service;
}
......@@ -131,11 +124,11 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
}
MessageToByteEncoder<Object> encoder = (MessageToByteEncoder<Object>) resolveEncoder(
request, elementType, mediaType, hints.toArray());
elementType, mediaType, hints.toArray());
if (encoder != null) {
Publisher<ByteBuffer> outputStream = encoder.encode(elementStream, type, mediaType, hints.toArray());
List<MessageToByteEncoder<ByteBuffer>> postProcessors = resolvePostProcessors(request,
List<MessageToByteEncoder<ByteBuffer>> postProcessors = resolvePostProcessors(
elementType, mediaType, hints.toArray());
for (MessageToByteEncoder<ByteBuffer> postProcessor : postProcessors) {
outputStream = postProcessor.encode(outputStream, elementType, mediaType, hints.toArray());
......@@ -155,9 +148,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
return ( mediaTypes.size() > 0 ? mediaTypes.get(0) : MediaType.TEXT_PLAIN);
}
private MessageToByteEncoder<?> resolveEncoder(ReactiveServerHttpRequest request,
ResolvableType type, MediaType mediaType, Object[] hints) {
private MessageToByteEncoder<?> resolveEncoder(ResolvableType type, MediaType mediaType, Object[] hints) {
for (MessageToByteEncoder<?> codec : this.serializers) {
if (codec.canEncode(type, mediaType, hints)) {
return codec;
......@@ -166,9 +157,8 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
return null;
}
private List<MessageToByteEncoder<ByteBuffer>> resolvePostProcessors(
ReactiveServerHttpRequest request, ResolvableType type, MediaType mediaType,
Object[] hints) {
private List<MessageToByteEncoder<ByteBuffer>> resolvePostProcessors(ResolvableType type,
MediaType mediaType, Object[] hints) {
List<MessageToByteEncoder<ByteBuffer>> postProcessors = new ArrayList<>();
for (MessageToByteEncoder<ByteBuffer> postProcessor : this.postProcessors) {
......
......@@ -21,17 +21,11 @@ import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.ResolvableType;
import reactor.io.buffer.Buffer;
import reactor.rx.Promise;
import reactor.rx.Promises;
......@@ -40,14 +34,22 @@ import reactor.rx.Streams;
import rx.Observable;
import rx.Single;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToReactorConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.reactive.codec.encoder.ByteBufferEncoder;
import org.springframework.reactive.codec.encoder.JacksonJsonEncoder;
import org.springframework.reactive.codec.encoder.JsonObjectEncoder;
import org.springframework.reactive.codec.encoder.StringEncoder;
import org.springframework.reactive.web.dispatch.DispatcherHandler;
import org.springframework.reactive.web.dispatch.SimpleHandlerResultHandler;
......@@ -59,7 +61,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.junit.Assert.assertEquals;
/**
* @author Rossen Stoyanchev
......@@ -68,26 +72,17 @@ import org.springframework.web.context.support.StaticWebApplicationContext;
*/
public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrationTests {
private TestController controller;
private AnnotationConfigWebApplicationContext wac;
@Override
protected HttpHandler createHttpHandler() {
StaticWebApplicationContext wac = new StaticWebApplicationContext();
DefaultListableBeanFactory factory = wac.getDefaultListableBeanFactory();
wac.registerSingleton("handlerMapping", RequestMappingHandlerMapping.class);
wac.registerSingleton("handlerAdapter", RequestMappingHandlerAdapter.class);
factory.registerSingleton("responseBodyResultHandler", new ResponseBodyResultHandler(
Arrays.asList(new ByteBufferEncoder(), new StringEncoder(),new JacksonJsonEncoder()),
Collections.singletonList(new JsonObjectEncoder())));
wac.registerSingleton("simpleResultHandler", SimpleHandlerResultHandler.class);
this.controller = new TestController();
factory.registerSingleton("controller", this.controller);
wac.refresh();
this.wac = new AnnotationConfigWebApplicationContext();
this.wac.register(FrameworkConfig.class, ApplicationConfig.class);
this.wac.refresh();
DispatcherHandler dispatcherHandler = new DispatcherHandler();
dispatcherHandler.setApplicationContext(wac);
dispatcherHandler.setApplicationContext(this.wac);
return dispatcherHandler;
}
......@@ -212,11 +207,11 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
ResponseEntity<Void> response = restTemplate.exchange(request, Void.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(2, this.controller.persons.size());
assertEquals(2, this.wac.getBean(TestController.class).persons.size());
}
public void serializeAsPojo(String requestUrl) throws Exception {
private void serializeAsPojo(String requestUrl) throws Exception {
RestTemplate restTemplate = new RestTemplate();
RequestEntity<Void> request = RequestEntity.get(new URI(requestUrl))
.accept(MediaType.APPLICATION_JSON)
......@@ -226,17 +221,7 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
assertEquals(new Person("Robert"), response.getBody());
}
public void postAsPojo(String requestUrl) throws Exception {
RestTemplate restTemplate = new RestTemplate();
RequestEntity<Person> request = RequestEntity.post(new URI(requestUrl))
.accept(MediaType.APPLICATION_JSON)
.body(new Person("Robert"));
ResponseEntity<Person> response = restTemplate.exchange(request, Person.class);
assertEquals(new Person("Robert"), response.getBody());
}
public void serializeAsCollection(String requestUrl) throws Exception {
private void serializeAsCollection(String requestUrl) throws Exception {
RestTemplate restTemplate = new RestTemplate();
RequestEntity<Void> request = RequestEntity.get(new URI(requestUrl))
.accept(MediaType.APPLICATION_JSON)
......@@ -250,7 +235,7 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
}
public void capitalizePojo(String requestUrl) throws Exception {
private void capitalizePojo(String requestUrl) throws Exception {
RestTemplate restTemplate = new RestTemplate();
RequestEntity<Person> request = RequestEntity.post(new URI(requestUrl))
.contentType(MediaType.APPLICATION_JSON)
......@@ -261,8 +246,7 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
assertEquals(new Person("ROBERT"), response.getBody());
}
public void capitalizeCollection(String requestUrl) throws Exception {
private void capitalizeCollection(String requestUrl) throws Exception {
RestTemplate restTemplate = new RestTemplate();
RequestEntity<List<Person>> request = RequestEntity.post(new URI(requestUrl))
.contentType(MediaType.APPLICATION_JSON)
......@@ -277,6 +261,57 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
}
@Configuration
@SuppressWarnings("unused")
static class FrameworkConfig {
@Bean
public RequestMappingHandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
@Bean
public RequestMappingHandlerAdapter handlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
handlerAdapter.setConversionService(conversionService());
return handlerAdapter;
}
@Bean
public ConversionService conversionService() {
// TODO: test failures with DefaultConversionService
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToReactorConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
return service;
}
@Bean
public ResponseBodyResultHandler responseBodyResultHandler() {
return new ResponseBodyResultHandler(Arrays.asList(
new ByteBufferEncoder(), new StringEncoder(),new JacksonJsonEncoder()),
conversionService());
}
@Bean
public SimpleHandlerResultHandler simpleHandlerResultHandler() {
return new SimpleHandlerResultHandler();
}
}
@Configuration
@SuppressWarnings("unused")
static class ApplicationConfig {
@Bean
public TestController testController() {
return new TestController();
}
}
@Controller
@SuppressWarnings("unused")
private static class TestController {
......@@ -427,6 +462,7 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
private String name;
@SuppressWarnings("unused")
public Person() {
}
......@@ -444,12 +480,19 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
@Override
public boolean equals(Object o) {
return name.equals(((Person)o).name);
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
return !(this.name != null ? !this.name.equals(person.name) : person.name != null);
}
@Override
public int hashCode() {
return name.hashCode();
return this.name != null ? this.name.hashCode() : 0;
}
}
......
......@@ -18,15 +18,18 @@ package org.springframework.reactive.web.dispatch.method.annotation;
import java.util.Collections;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.reactivestreams.Publisher;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.reactive.codec.encoder.StringEncoder;
import org.springframework.reactive.web.dispatch.HandlerResult;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Sebastien Deleuze
*/
......@@ -35,17 +38,18 @@ public class ResponseBodyResultHandlerTests {
@Test
public void supports() throws NoSuchMethodException {
ResponseBodyResultHandler resultHandler = new ResponseBodyResultHandler(Collections.emptyList());
ResponseBodyResultHandler handler = new ResponseBodyResultHandler(Collections.singletonList(
new StringEncoder()), new DefaultConversionService());
TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller,TestController.class.getMethod("notAnnotated"));
assertFalse(resultHandler.supports(new HandlerResult(hm, null)));
assertFalse(handler.supports(new HandlerResult(hm, null)));
hm = new HandlerMethod(controller, TestController.class.getMethod("publisherString"));
assertTrue(resultHandler.supports(new HandlerResult(hm, null)));
assertTrue(handler.supports(new HandlerResult(hm, null)));
hm = new HandlerMethod(controller, TestController.class.getMethod("publisherVoid"));
assertTrue(resultHandler.supports(new HandlerResult(hm, null)));
assertTrue(handler.supports(new HandlerResult(hm, null)));
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册