From 911d3769224d009fb9fe08c6cb5489686bbc5713 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Sun, 22 Nov 2015 14:41:59 -0500 Subject: [PATCH] Improve MimeType -> MediaType handling This change adds an Encoder-to-MediaType lookup in ResponseBodyResultHandler to avoid MimeType to MediaType creation at runtime. --- .../annotation/ResponseBodyResultHandler.java | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java index 95e511979a..326014b98c 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/method/annotation/ResponseBodyResultHandler.java @@ -20,9 +20,12 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.reactivestreams.Publisher; import reactor.Publishers; @@ -35,13 +38,13 @@ import org.springframework.http.MediaType; import org.springframework.http.server.ReactiveServerHttpRequest; import org.springframework.http.server.ReactiveServerHttpResponse; import org.springframework.reactive.codec.encoder.Encoder; -import org.springframework.web.reactive.HandlerResult; -import org.springframework.web.reactive.HandlerResultHandler; import org.springframework.util.Assert; import org.springframework.util.MimeType; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; +import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.reactive.HandlerResultHandler; /** @@ -53,12 +56,14 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); - private final List allSupportedMediaTypes; - private final List> encoders; private final ConversionService conversionService; + private final List allMediaTypes; + + private final Map, List> mediaTypesByEncoder; + private int order = 0; @@ -66,23 +71,39 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered Assert.notEmpty(encoders, "At least one encoders is required."); Assert.notNull(service, "'conversionService' is required."); this.encoders = encoders; - this.allSupportedMediaTypes = getAllSupportedMediaTypes(encoders); this.conversionService = service; + this.allMediaTypes = getAllMediaTypes(encoders); + this.mediaTypesByEncoder = getMediaTypesByEncoder(encoders); } - private static List getAllSupportedMediaTypes(List> encoders) { - Set allSupportedMediaTypes = new LinkedHashSet<>(); - for (Encoder encoder : encoders) { - for (MimeType mimeType : encoder.getSupportedMimeTypes()) { - allSupportedMediaTypes.add( - new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters())); - } - } - List result = new ArrayList<>(allSupportedMediaTypes); + private static List getAllMediaTypes(List> encoders) { + Set set = new LinkedHashSet<>(); + encoders.forEach(encoder -> set.addAll(toMediaTypes(encoder.getSupportedMimeTypes()))); + List result = new ArrayList<>(set); MediaType.sortBySpecificity(result); return Collections.unmodifiableList(result); } + private static Map, List> getMediaTypesByEncoder(List> encoders) { + Map, List> result = new HashMap<>(encoders.size()); + encoders.forEach(encoder -> result.put(encoder, toMediaTypes(encoder.getSupportedMimeTypes()))); + return Collections.unmodifiableMap(result); + } + + /** + * TODO: MediaType static method + */ + private static List toMediaTypes(List mimeTypes) { + return mimeTypes.stream().map(ResponseBodyResultHandler::toMediaType).collect(Collectors.toList()); + } + + /** + * TODO: MediaType constructor + */ + private static MediaType toMediaType(MimeType mimeType) { + return new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters()); + } + public void setOrder(int order) { this.order = order; @@ -120,8 +141,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered List producibleMediaTypes = getProducibleMediaTypes(returnType); if (producibleMediaTypes.isEmpty()) { - Publishers.error(new IllegalArgumentException( - "No encoder found for return value of type: " + returnType)); + producibleMediaTypes.add(MediaType.ALL); } Set compatibleMediaTypes = new LinkedHashSet<>(); @@ -169,7 +189,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered } } - return Publishers.error(new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes)); + return Publishers.error(new HttpMediaTypeNotAcceptableException(this.allMediaTypes)); } private List getAcceptableMediaTypes(ReactiveServerHttpRequest request) { @@ -178,19 +198,10 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered } private List getProducibleMediaTypes(ResolvableType type) { - List result = new ArrayList<>(); - for (Encoder encoder : this.encoders) { - if (encoder.canEncode(type, null)) { - for (MimeType mimeType : encoder.getSupportedMimeTypes()) { - result.add(new MediaType(mimeType.getType(), mimeType.getSubtype(), - mimeType.getParameters())); - } - } - } - if (result.isEmpty()) { - result.add(MediaType.ALL); - } - return result; + return this.encoders.stream() + .filter(encoder -> encoder.canEncode(type, null)) + .flatMap(encoder -> this.mediaTypesByEncoder.get(encoder).stream()) + .collect(Collectors.toList()); } /**