Improve MimeType -> MediaType handling

This change adds an Encoder-to-MediaType lookup in
ResponseBodyResultHandler to avoid MimeType to MediaType creation at
runtime.
This commit is contained in:
Rossen Stoyanchev
2015-11-22 14:41:59 -05:00
parent 8d30722f21
commit 911d376922

View File

@@ -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<MediaType> allSupportedMediaTypes;
private final List<Encoder<?>> encoders;
private final ConversionService conversionService;
private final List<MediaType> allMediaTypes;
private final Map<Encoder<?>, List<MediaType>> 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<MediaType> getAllSupportedMediaTypes(List<Encoder<?>> encoders) {
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<>();
for (Encoder<?> encoder : encoders) {
for (MimeType mimeType : encoder.getSupportedMimeTypes()) {
allSupportedMediaTypes.add(
new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters()));
}
}
List<MediaType> result = new ArrayList<>(allSupportedMediaTypes);
private static List<MediaType> getAllMediaTypes(List<Encoder<?>> encoders) {
Set<MediaType> set = new LinkedHashSet<>();
encoders.forEach(encoder -> set.addAll(toMediaTypes(encoder.getSupportedMimeTypes())));
List<MediaType> result = new ArrayList<>(set);
MediaType.sortBySpecificity(result);
return Collections.unmodifiableList(result);
}
private static Map<Encoder<?>, List<MediaType>> getMediaTypesByEncoder(List<Encoder<?>> encoders) {
Map<Encoder<?>, List<MediaType>> 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<MediaType> toMediaTypes(List<MimeType> 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<MediaType> producibleMediaTypes = getProducibleMediaTypes(returnType);
if (producibleMediaTypes.isEmpty()) {
Publishers.error(new IllegalArgumentException(
"No encoder found for return value of type: " + returnType));
producibleMediaTypes.add(MediaType.ALL);
}
Set<MediaType> 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<MediaType> getAcceptableMediaTypes(ReactiveServerHttpRequest request) {
@@ -178,19 +198,10 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
}
private List<MediaType> getProducibleMediaTypes(ResolvableType type) {
List<MediaType> 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());
}
/**