Browse Source

Optimize some iterations in BodyExtractor and BodyInserter

This commit turns some stream-based iterations back into simpler
enhanced for loops.

For simple use cases like these, where the stream API is merely used to
map/filter + collect to a List, a for loop is more efficient.
This is especially true for small collections like the ones we deal
with in BodyInserters/BodyExtractors here (in the order of 50ns/op vs
5ns/op). These cases are also simple enough that they don't lose in
readability after the conversion.

Closes gh-30136
pull/30161/head
James Yuzawa 3 years ago committed by GitHub
parent
commit
800b13492b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java
  2. 35
      spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java
  3. 26
      spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java

6
spring-web/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java

@ -147,8 +147,10 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
this.commitActions.add(writeAction); this.commitActions.add(writeAction);
} }
List<? extends Publisher<Void>> actions = this.commitActions.stream() List<Publisher<Void>> actions = new ArrayList<>(this.commitActions.size());
.map(Supplier::get).toList(); for (Supplier<? extends Publisher<Void>> commitAction : this.commitActions) {
actions.add(commitAction.get());
}
return Flux.concat(actions).then(); return Flux.concat(actions).then();
} }

35
spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java

@ -194,18 +194,16 @@ public abstract class BodyExtractors {
MediaType contentType = Optional.ofNullable(message.getHeaders().getContentType()) MediaType contentType = Optional.ofNullable(message.getHeaders().getContentType())
.orElse(MediaType.APPLICATION_OCTET_STREAM); .orElse(MediaType.APPLICATION_OCTET_STREAM);
return context.messageReaders().stream() for (HttpMessageReader<?> messageReader : context.messageReaders()) {
.filter(reader -> reader.canRead(elementType, contentType)) if (messageReader.canRead(elementType, contentType)) {
.findFirst() return readerFunction.apply(cast(messageReader));
.map(BodyExtractors::<T>cast) }
.map(readerFunction) }
.orElseGet(() -> { List<MediaType> mediaTypes = context.messageReaders().stream()
List<MediaType> mediaTypes = context.messageReaders().stream() .flatMap(reader -> reader.getReadableMediaTypes(elementType).stream())
.flatMap(reader -> reader.getReadableMediaTypes(elementType).stream()) .toList();
.toList(); return errorFunction.apply(
return errorFunction.apply( new UnsupportedMediaTypeException(contentType, mediaTypes, elementType));
new UnsupportedMediaTypeException(contentType, mediaTypes, elementType));
});
} }
private static <T> Mono<T> readToMono(ReactiveHttpInputMessage message, BodyExtractor.Context context, private static <T> Mono<T> readToMono(ReactiveHttpInputMessage message, BodyExtractor.Context context,
@ -245,12 +243,13 @@ public abstract class BodyExtractors {
private static <T> HttpMessageReader<T> findReader( private static <T> HttpMessageReader<T> findReader(
ResolvableType elementType, MediaType mediaType, BodyExtractor.Context context) { ResolvableType elementType, MediaType mediaType, BodyExtractor.Context context) {
return context.messageReaders().stream() for (HttpMessageReader<?> messageReader : context.messageReaders()) {
.filter(messageReader -> messageReader.canRead(elementType, mediaType)) if (messageReader.canRead(elementType, mediaType)) {
.findFirst() return cast(messageReader);
.map(BodyExtractors::<T>cast) }
.orElseThrow(() -> new IllegalStateException( }
"No HttpMessageReader for \"" + mediaType + "\" and \"" + elementType + "\"")); throw new IllegalStateException(
"No HttpMessageReader for \"" + mediaType + "\" and \"" + elementType + "\"");
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

26
spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyInserters.java

@ -373,12 +373,13 @@ public abstract class BodyInserters {
publisher = Mono.just(body); publisher = Mono.just(body);
} }
MediaType mediaType = outputMessage.getHeaders().getContentType(); MediaType mediaType = outputMessage.getHeaders().getContentType();
return context.messageWriters().stream() for (HttpMessageWriter<?> messageWriter : context.messageWriters()) {
.filter(messageWriter -> messageWriter.canWrite(bodyType, mediaType)) if (messageWriter.canWrite(bodyType, mediaType)) {
.findFirst() HttpMessageWriter<Object> typedMessageWriter = cast(messageWriter);
.map(BodyInserters::cast) return write(publisher, bodyType, mediaType, outputMessage, context, typedMessageWriter);
.map(writer -> write(publisher, bodyType, mediaType, outputMessage, context, writer)) }
.orElseGet(() -> Mono.error(unsupportedError(bodyType, context, mediaType))); }
return Mono.error(unsupportedError(bodyType, context, mediaType));
} }
private static UnsupportedMediaTypeException unsupportedError(ResolvableType bodyType, private static UnsupportedMediaTypeException unsupportedError(ResolvableType bodyType,
@ -406,12 +407,13 @@ public abstract class BodyInserters {
private static <T> HttpMessageWriter<T> findWriter( private static <T> HttpMessageWriter<T> findWriter(
BodyInserter.Context context, ResolvableType elementType, @Nullable MediaType mediaType) { BodyInserter.Context context, ResolvableType elementType, @Nullable MediaType mediaType) {
return context.messageWriters().stream() for (HttpMessageWriter<?> messageWriter : context.messageWriters()) {
.filter(messageWriter -> messageWriter.canWrite(elementType, mediaType)) if (messageWriter.canWrite(elementType, mediaType)) {
.findFirst() return cast(messageWriter);
.map(BodyInserters::<T>cast) }
.orElseThrow(() -> new IllegalStateException( }
"No HttpMessageWriter for \"" + mediaType + "\" and \"" + elementType + "\"")); throw new IllegalStateException(
"No HttpMessageWriter for \"" + mediaType + "\" and \"" + elementType + "\"");
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

Loading…
Cancel
Save