Use List<T> instead of Supplier<Stream<T>>

This commit changes all methods that return `Supplier<Stream<T>` to
return `List<T>` instead. This includes the HandlerStrategies, but also
BodyInserter.Context, BodyExtractor.Context, and ServerResponse.Context.

The reason for this change is that most of the strategies have some sort
of order, where more specific message readers - for instance - come
before generic ones. Such an ordering can only be enforced through a
List.

Issue: SPR-15578
This commit is contained in:
Arjen Poutsma 2017-05-24 13:48:03 +02:00
parent 01e3561db9
commit 31d1e26c95
28 changed files with 150 additions and 188 deletions

View File

@ -16,10 +16,9 @@
package org.springframework.web.reactive.function; package org.springframework.web.reactive.function;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.http.ReactiveHttpInputMessage; import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
@ -52,11 +51,10 @@ public interface BodyExtractor<T, M extends ReactiveHttpInputMessage> {
interface Context { interface Context {
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageReader}s * Return the {@link HttpMessageReader}s to be used for body extraction.
* to be used for body extraction.
* @return the stream of message readers * @return the stream of message readers
*/ */
Supplier<Stream<HttpMessageReader<?>>> messageReaders(); List<HttpMessageReader<?>> messageReaders();
/** /**
* Optionally return the {@link ServerHttpResponse}, if present. * Optionally return the {@link ServerHttpResponse}, if present.

View File

@ -19,9 +19,7 @@ package org.springframework.web.reactive.function;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
@ -197,14 +195,14 @@ public abstract class BodyExtractors {
Function<HttpMessageReader<T>, S> readerFunction, Function<Throwable, S> unsupportedError) { Function<HttpMessageReader<T>, S> readerFunction, Function<Throwable, S> unsupportedError) {
MediaType contentType = contentType(inputMessage); MediaType contentType = contentType(inputMessage);
Supplier<Stream<HttpMessageReader<?>>> messageReaders = context.messageReaders(); List<HttpMessageReader<?>> messageReaders = context.messageReaders();
return messageReaders.get() return messageReaders.stream()
.filter(r -> r.canRead(elementType, contentType)) .filter(r -> r.canRead(elementType, contentType))
.findFirst() .findFirst()
.map(BodyExtractors::<T>cast) .map(BodyExtractors::<T>cast)
.map(readerFunction) .map(readerFunction)
.orElseGet(() -> { .orElseGet(() -> {
List<MediaType> supportedMediaTypes = messageReaders.get() List<MediaType> supportedMediaTypes = messageReaders.stream()
.flatMap(reader -> reader.getReadableMediaTypes().stream()) .flatMap(reader -> reader.getReadableMediaTypes().stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
UnsupportedMediaTypeException error = UnsupportedMediaTypeException error =
@ -215,7 +213,7 @@ public abstract class BodyExtractors {
private static <T> HttpMessageReader<T> messageReader(ResolvableType elementType, private static <T> HttpMessageReader<T> messageReader(ResolvableType elementType,
MediaType mediaType, BodyExtractor.Context context) { MediaType mediaType, BodyExtractor.Context context) {
return context.messageReaders().get() return context.messageReaders().stream()
.filter(messageReader -> messageReader.canRead(elementType, mediaType)) .filter(messageReader -> messageReader.canRead(elementType, mediaType))
.findFirst() .findFirst()
.map(BodyExtractors::<T>cast) .map(BodyExtractors::<T>cast)

View File

@ -16,10 +16,9 @@
package org.springframework.web.reactive.function; package org.springframework.web.reactive.function;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -54,11 +53,10 @@ public interface BodyInserter<T, M extends ReactiveHttpOutputMessage> {
interface Context { interface Context {
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageWriter}s * Return the {@link HttpMessageWriter}s to be used for response body conversion.
* to be used for response body conversion.
* @return the stream of message writers * @return the stream of message writers
*/ */
Supplier<Stream<HttpMessageWriter<?>>> messageWriters(); List<HttpMessageWriter<?>> messageWriters();
/** /**
* Optionally return the {@link ServerHttpRequest}, if present. * Optionally return the {@link ServerHttpRequest}, if present.

View File

@ -18,9 +18,7 @@ package org.springframework.web.reactive.function;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -141,7 +139,7 @@ public abstract class BodyInserters {
} }
private static HttpMessageWriter<Resource> resourceHttpMessageWriter(BodyInserter.Context context) { private static HttpMessageWriter<Resource> resourceHttpMessageWriter(BodyInserter.Context context) {
return context.messageWriters().get() return context.messageWriters().stream()
.filter(messageWriter -> messageWriter.canWrite(RESOURCE_TYPE, null)) .filter(messageWriter -> messageWriter.canWrite(RESOURCE_TYPE, null))
.findFirst() .findFirst()
.map(BodyInserters::<Resource>cast) .map(BodyInserters::<Resource>cast)
@ -288,8 +286,8 @@ public abstract class BodyInserters {
return (outputMessage, context) -> { return (outputMessage, context) -> {
MediaType contentType = outputMessage.getHeaders().getContentType(); MediaType contentType = outputMessage.getHeaders().getContentType();
Supplier<Stream<HttpMessageWriter<?>>> messageWriters = context.messageWriters(); List<HttpMessageWriter<?>> messageWriters = context.messageWriters();
return messageWriters.get() return messageWriters.stream()
.filter(messageWriter -> messageWriter.canWrite(bodyType, contentType)) .filter(messageWriter -> messageWriter.canWrite(bodyType, contentType))
.findFirst() .findFirst()
.map(BodyInserters::cast) .map(BodyInserters::cast)
@ -305,7 +303,7 @@ public abstract class BodyInserters {
} }
}) })
.orElseGet(() -> { .orElseGet(() -> {
List<MediaType> supportedMediaTypes = messageWriters.get() List<MediaType> supportedMediaTypes = messageWriters.stream()
.flatMap(reader -> reader.getWritableMediaTypes().stream()) .flatMap(reader -> reader.getWritableMediaTypes().stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
UnsupportedMediaTypeException error = UnsupportedMediaTypeException error =
@ -318,7 +316,7 @@ public abstract class BodyInserters {
private static <T> HttpMessageWriter<T> findMessageWriter( private static <T> HttpMessageWriter<T> findMessageWriter(
BodyInserter.Context context, ResolvableType type, MediaType mediaType) { BodyInserter.Context context, ResolvableType type, MediaType mediaType) {
return context.messageWriters().get() return context.messageWriters().stream()
.filter(messageWriter -> messageWriter.canWrite(type, mediaType)) .filter(messageWriter -> messageWriter.canWrite(type, mediaType))
.findFirst() .findFirst()
.map(BodyInserters::<T>cast) .map(BodyInserters::<T>cast)

View File

@ -18,10 +18,9 @@ package org.springframework.web.reactive.function.client;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -187,7 +186,7 @@ class DefaultClientRequestBuilder implements ClientRequest.Builder {
return this.inserter.insert(request, new BodyInserter.Context() { return this.inserter.insert(request, new BodyInserter.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return strategies.messageWriters(); return strategies.messageWriters();
} }

View File

@ -21,8 +21,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.function.Supplier;
import java.util.stream.Stream;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -79,7 +77,7 @@ class DefaultClientResponse implements ClientResponse {
public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) { public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) {
return extractor.extract(this.response, new BodyExtractor.Context() { return extractor.extract(this.response, new BodyExtractor.Context() {
@Override @Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() { public List<HttpMessageReader<?>> messageReaders() {
return strategies.messageReaders(); return strategies.messageReaders();
} }

View File

@ -20,8 +20,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.http.codec.ClientCodecConfigurer; import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
@ -88,13 +86,13 @@ class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Builder {
} }
@Override @Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() { public List<HttpMessageReader<?>> messageReaders() {
return this.messageReaders::stream; return this.messageReaders;
} }
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return this.messageWriters::stream; return this.messageWriters;
} }
} }

View File

@ -16,9 +16,8 @@
package org.springframework.web.reactive.function.client; package org.springframework.web.reactive.function.client;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.http.codec.ClientCodecConfigurer; import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
@ -28,8 +27,7 @@ import org.springframework.http.codec.HttpMessageWriter;
* Defines the strategies for invoking {@link ExchangeFunction}s. An instance of * Defines the strategies for invoking {@link ExchangeFunction}s. An instance of
* this class is immutable; instances are typically created through the mutable {@link Builder}: * this class is immutable; instances are typically created through the mutable {@link Builder}:
* either through {@link #builder()} to set up default strategies, or {@link #empty()} to start * either through {@link #builder()} to set up default strategies, or {@link #empty()} to start
* from scratch. Alternatively, {@code ExchangeStrategies} instances can be created through * from scratch.
* {@link #of(Supplier, Supplier)}.
* *
* @author Brian Clozel * @author Brian Clozel
* @author Arjen Poutsma * @author Arjen Poutsma
@ -40,18 +38,16 @@ public interface ExchangeStrategies {
// Instance methods // Instance methods
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageReader}s to be used for request * Return the {@link HttpMessageReader}s to be used for request body conversion.
* body conversion.
* @return the stream of message readers * @return the stream of message readers
*/ */
Supplier<Stream<HttpMessageReader<?>>> messageReaders(); List<HttpMessageReader<?>> messageReaders();
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageWriter}s to be used for response * Return the {@link HttpMessageWriter}s to be used for response body conversion.
* body conversion.
* @return the stream of message writers * @return the stream of message writers
*/ */
Supplier<Stream<HttpMessageWriter<?>>> messageWriters(); List<HttpMessageWriter<?>> messageWriters();
// Static methods // Static methods

View File

@ -23,11 +23,10 @@ import java.time.format.DateTimeFormatter;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -208,7 +207,7 @@ class DefaultEntityResponseBuilder<T> implements EntityResponse.Builder<T> {
writeStatusAndHeaders(response); writeStatusAndHeaders(response);
return inserter().insert(response, new BodyInserter.Context() { return inserter().insert(response, new BodyInserter.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return context.messageWriters(); return context.messageWriters();
} }

View File

@ -20,8 +20,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.CodecConfigurer;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
@ -135,28 +133,28 @@ class DefaultHandlerStrategiesBuilder implements HandlerStrategies.Builder {
} }
@Override @Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() { public List<HttpMessageReader<?>> messageReaders() {
return this.messageReaders::stream; return this.messageReaders;
} }
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return this.messageWriters::stream; return this.messageWriters;
} }
@Override @Override
public Supplier<Stream<ViewResolver>> viewResolvers() { public List<ViewResolver> viewResolvers() {
return this.viewResolvers::stream; return this.viewResolvers;
} }
@Override @Override
public Supplier<Stream<WebFilter>> webFilters() { public List<WebFilter> webFilters() {
return this.webFilters::stream; return this.webFilters;
} }
@Override @Override
public Supplier<Stream<WebExceptionHandler>> exceptionHandlers() { public List<WebExceptionHandler> exceptionHandlers() {
return this.exceptionHandlers::stream; return this.exceptionHandlers;
} }
} }

View File

@ -164,7 +164,7 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder {
writeStatusAndHeaders(response); writeStatusAndHeaders(response);
MediaType contentType = exchange.getResponse().getHeaders().getContentType(); MediaType contentType = exchange.getResponse().getHeaders().getContentType();
Locale locale = resolveLocale(exchange); Locale locale = resolveLocale(exchange);
Stream<ViewResolver> viewResolverStream = context.viewResolvers().get(); Stream<ViewResolver> viewResolverStream = context.viewResolvers().stream();
return Flux.fromStream(viewResolverStream) return Flux.fromStream(viewResolverStream)
.concatMap(viewResolver -> viewResolver.resolveViewName(name(), locale)) .concatMap(viewResolver -> viewResolver.resolveViewName(name(), locale))

View File

@ -20,6 +20,7 @@ import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.Principal; import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -27,8 +28,6 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -66,15 +65,20 @@ class DefaultServerRequest implements ServerRequest {
private final Headers headers; private final Headers headers;
private final Supplier<Stream<HttpMessageReader<?>>> messageReaders; private final List<HttpMessageReader<?>> messageReaders;
DefaultServerRequest(ServerWebExchange exchange, Supplier<Stream<HttpMessageReader<?>>> messageReaders) { DefaultServerRequest(ServerWebExchange exchange, List<HttpMessageReader<?>> messageReaders) {
this.exchange = exchange; this.exchange = exchange;
this.messageReaders = messageReaders; this.messageReaders = unmodifiableCopy(messageReaders);
this.headers = new DefaultHeaders(); this.headers = new DefaultHeaders();
} }
private static <T> List<T> unmodifiableCopy(List<? extends T> list) {
return Collections.unmodifiableList(new ArrayList<>(list));
}
@Override @Override
public HttpMethod method() { public HttpMethod method() {
@ -102,8 +106,8 @@ class DefaultServerRequest implements ServerRequest {
return extractor.extract(request(), return extractor.extract(request(),
new BodyExtractor.Context() { new BodyExtractor.Context() {
@Override @Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() { public List<HttpMessageReader<?>> messageReaders() {
return DefaultServerRequest.this.messageReaders; return messageReaders;
} }
@Override @Override
public Optional<ServerHttpResponse> serverResponse() { public Optional<ServerHttpResponse> serverResponse() {

View File

@ -23,12 +23,11 @@ import java.time.format.DateTimeFormatter;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -314,7 +313,7 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
writeStatusAndHeaders(response); writeStatusAndHeaders(response);
return this.inserter.insert(response, new BodyInserter.Context() { return this.inserter.insert(response, new BodyInserter.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return context.messageWriters(); return context.messageWriters();
} }

View File

@ -16,9 +16,8 @@
package org.springframework.web.reactive.function.server; package org.springframework.web.reactive.function.server;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.HttpMessageWriter;
@ -43,39 +42,34 @@ public interface HandlerStrategies {
// Instance methods // Instance methods
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageReader}s to be used for request * Return the {@link HttpMessageReader}s to be used for request body conversion.
* body conversion. * @return the message readers
* @return the stream of message readers
*/ */
Supplier<Stream<HttpMessageReader<?>>> messageReaders(); List<HttpMessageReader<?>> messageReaders();
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageWriter}s to be used for response * Return the {@link HttpMessageWriter}s to be used for response body conversion.
* body conversion. * @return the message writers
* @return the stream of message writers
*/ */
Supplier<Stream<HttpMessageWriter<?>>> messageWriters(); List<HttpMessageWriter<?>> messageWriters();
/** /**
* Supply a {@linkplain Stream stream} of {@link ViewResolver}s to be used for view name * Return the {@link ViewResolver}s to be used for view name resolution.
* resolution. * @return the view resolvers
* @return the stream of view resolvers
*/ */
Supplier<Stream<ViewResolver>> viewResolvers(); List<ViewResolver> viewResolvers();
/** /**
* Supply a {@linkplain Stream stream} of {@link WebFilter}s to be used for filtering the * Return the {@link WebFilter}s to be used for filtering the request and response.
* request and response. * @return the web filters
* @return the stream of web filters
*/ */
Supplier<Stream<WebFilter>> webFilters(); List<WebFilter> webFilters();
/** /**
* Supply a {@linkplain Stream stream} of {@link WebExceptionHandler}s to be used for handling * Return the {@link WebExceptionHandler}s to be used for handling exceptions.
* exceptions. * @return the exception handlers
* @return the stream of exception handlers
*/ */
Supplier<Stream<WebExceptionHandler>> exceptionHandlers(); List<WebExceptionHandler> exceptionHandlers();
// Static methods // Static methods

View File

@ -16,10 +16,10 @@
package org.springframework.web.reactive.function.server; package org.springframework.web.reactive.function.server;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -45,9 +45,6 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
* <p>Additionally, this class can {@linkplain #toHttpHandler(RouterFunction) transform} a * <p>Additionally, this class can {@linkplain #toHttpHandler(RouterFunction) transform} a
* {@code RouterFunction} into an {@code HttpHandler}, which can be run in Servlet 3.1+, * {@code RouterFunction} into an {@code HttpHandler}, which can be run in Servlet 3.1+,
* Reactor, or Undertow. * Reactor, or Undertow.
* And it can {@linkplain #toHandlerMapping(RouterFunction, HandlerStrategies) transform} a
* {@code RouterFunction} into an {@code HandlerMapping}, which can be run in a
* {@code DispatcherHandler}.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 5.0 * @since 5.0
@ -199,10 +196,10 @@ public abstract class RouterFunctions {
Assert.notNull(strategies, "HandlerStrategies must not be null"); Assert.notNull(strategies, "HandlerStrategies must not be null");
WebHandler webHandler = toWebHandler(routerFunction, strategies); WebHandler webHandler = toWebHandler(routerFunction, strategies);
WebHttpHandlerBuilder handlerBuilder = WebHttpHandlerBuilder.webHandler(webHandler); return WebHttpHandlerBuilder.webHandler(webHandler)
strategies.webFilters().get().forEach(handlerBuilder::filter); .filters(strategies.webFilters())
strategies.exceptionHandlers().get().forEach(handlerBuilder::exceptionHandler); .exceptionHandlers(strategies.exceptionHandlers())
return handlerBuilder.build(); .build();
} }
/** /**
@ -222,7 +219,8 @@ public abstract class RouterFunctions {
return routerFunction.route(request) return routerFunction.route(request)
.defaultIfEmpty(notFound()) .defaultIfEmpty(notFound())
.flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request))) .flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request)))
.flatMap(response -> wrapException(() -> response.writeTo(exchange, toContext(strategies)))); .flatMap(response -> wrapException(() -> response.writeTo(exchange,
new HandlerStrategiesResponseContext(strategies))));
}; };
} }
@ -235,20 +233,6 @@ public abstract class RouterFunctions {
} }
} }
private static ServerResponse.Context toContext(HandlerStrategies strategies) {
return new ServerResponse.Context() {
@Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
return strategies.messageWriters();
}
@Override
public Supplier<Stream<ViewResolver>> viewResolvers() {
return strategies.viewResolvers();
}
};
}
private static void addAttributes(ServerWebExchange exchange, ServerRequest request) { private static void addAttributes(ServerWebExchange exchange, ServerRequest request) {
Map<String, Object> attributes = exchange.getAttributes(); Map<String, Object> attributes = exchange.getAttributes();
attributes.put(REQUEST_ATTRIBUTE, request); attributes.put(REQUEST_ATTRIBUTE, request);
@ -332,4 +316,23 @@ public abstract class RouterFunctions {
} }
} }
private static class HandlerStrategiesResponseContext implements ServerResponse.Context {
private final HandlerStrategies strategies;
public HandlerStrategiesResponseContext(HandlerStrategies strategies) {
this.strategies = strategies;
}
@Override
public List<HttpMessageWriter<?>> messageWriters() {
return this.strategies.messageWriters();
}
@Override
public List<ViewResolver> viewResolvers() {
return this.strategies.viewResolvers();
}
}
} }

View File

@ -187,7 +187,7 @@ public interface ServerRequest {
static ServerRequest create(ServerWebExchange exchange, static ServerRequest create(ServerWebExchange exchange,
List<HttpMessageReader<?>> messageReaders) { List<HttpMessageReader<?>> messageReaders) {
return new DefaultServerRequest(exchange, messageReaders::stream); return new DefaultServerRequest(exchange, messageReaders);
} }
/** /**

View File

@ -19,11 +19,10 @@ package org.springframework.web.reactive.function.server;
import java.net.URI; import java.net.URI;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -395,18 +394,16 @@ public interface ServerResponse {
interface Context { interface Context {
/** /**
* Supply a {@linkplain Stream stream} of {@link HttpMessageWriter}s * Return the {@link HttpMessageWriter}s to be used for response body conversion.
* to be used for response body conversion.
* @return the stream of message writers * @return the stream of message writers
*/ */
Supplier<Stream<HttpMessageWriter<?>>> messageWriters(); List<HttpMessageWriter<?>> messageWriters();
/** /**
* Supply a {@linkplain Stream stream} of {@link ViewResolver}s to be used for view name * Return the {@link ViewResolver}s to be used for view name resolution.
* resolution.
* @return the stream of view resolvers * @return the stream of view resolvers
*/ */
Supplier<Stream<ViewResolver>> viewResolvers(); List<ViewResolver> viewResolvers();
} }

View File

@ -18,8 +18,6 @@ package org.springframework.web.reactive.function.server.support;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -99,13 +97,13 @@ public class ServerResponseResultHandler implements HandlerResultHandler, Initia
Assert.state(response != null, "No ServerResponse"); Assert.state(response != null, "No ServerResponse");
return response.writeTo(exchange, new ServerResponse.Context() { return response.writeTo(exchange, new ServerResponse.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return messageCodecConfigurer.getWriters()::stream; return messageCodecConfigurer.getWriters();
} }
@Override @Override
public Supplier<Stream<ViewResolver>> viewResolvers() { public List<ViewResolver> viewResolvers() {
return viewResolvers::stream; return viewResolvers;
} }
}); });
} }

View File

@ -24,8 +24,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.annotation.JsonView;
import org.junit.Before; import org.junit.Before;
@ -86,8 +84,8 @@ public class BodyExtractorsTests {
this.context = new BodyExtractor.Context() { this.context = new BodyExtractor.Context() {
@Override @Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() { public List<HttpMessageReader<?>> messageReaders() {
return messageReaders::stream; return messageReaders;
} }
@Override @Override
@ -209,8 +207,8 @@ public class BodyExtractorsTests {
BodyExtractor.Context emptyContext = new BodyExtractor.Context() { BodyExtractor.Context emptyContext = new BodyExtractor.Context() {
@Override @Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() { public List<HttpMessageReader<?>> messageReaders() {
return Stream::empty; return Collections.emptyList();
} }
@Override @Override

View File

@ -26,8 +26,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.annotation.JsonView;
import org.junit.Before; import org.junit.Before;
@ -93,8 +91,8 @@ public class BodyInsertersTests {
this.context = new BodyInserter.Context() { this.context = new BodyInserter.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return messageWriters::stream; return messageWriters;
} }
@Override @Override
@ -207,8 +205,8 @@ public class BodyInsertersTests {
MockServerHttpResponse response = new MockServerHttpResponse(); MockServerHttpResponse response = new MockServerHttpResponse();
Mono<Void> result = inserter.insert(response, new BodyInserter.Context() { Mono<Void> result = inserter.insert(response, new BodyInserter.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return Collections.<HttpMessageWriter<?>>singleton(new ResourceHttpMessageWriter())::stream; return Collections.singletonList(new ResourceHttpMessageWriter());
} }
@Override @Override

View File

@ -34,10 +34,12 @@ import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.mock.http.client.reactive.test.MockClientHttpRequest; import org.springframework.mock.http.client.reactive.test.MockClientHttpRequest;
import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserter;
import static java.nio.charset.StandardCharsets.*; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.springframework.http.HttpMethod.*; import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
@ -107,7 +109,7 @@ public class DefaultClientRequestBuilderTests {
messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes())); messageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
ExchangeStrategies strategies = mock(ExchangeStrategies.class); ExchangeStrategies strategies = mock(ExchangeStrategies.class);
when(strategies.messageWriters()).thenReturn(messageWriters::stream); when(strategies.messageWriters()).thenReturn(messageWriters);
MockClientHttpRequest request = new MockClientHttpRequest(GET, "/"); MockClientHttpRequest request = new MockClientHttpRequest(GET, "/");
result.writeTo(request, strategies).block(); result.writeTo(request, strategies).block();

View File

@ -23,7 +23,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.Set;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -121,9 +120,9 @@ public class DefaultClientResponseTests {
when(mockResponse.getHeaders()).thenReturn(httpHeaders); when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getBody()).thenReturn(body); when(mockResponse.getBody()).thenReturn(body);
Set<HttpMessageReader<?>> messageReaders = Collections List<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true))); .singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true)));
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders);
Mono<String> resultMono = defaultClientResponse.body(toMono(String.class)); Mono<String> resultMono = defaultClientResponse.body(toMono(String.class));
assertEquals("foo", resultMono.block()); assertEquals("foo", resultMono.block());
@ -142,9 +141,9 @@ public class DefaultClientResponseTests {
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getBody()).thenReturn(body); when(mockResponse.getBody()).thenReturn(body);
Set<HttpMessageReader<?>> messageReaders = Collections List<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true))); .singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true)));
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders);
Mono<String> resultMono = defaultClientResponse.bodyToMono(String.class); Mono<String> resultMono = defaultClientResponse.bodyToMono(String.class);
assertEquals("foo", resultMono.block()); assertEquals("foo", resultMono.block());
@ -163,9 +162,9 @@ public class DefaultClientResponseTests {
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK); when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getBody()).thenReturn(body); when(mockResponse.getBody()).thenReturn(body);
Set<HttpMessageReader<?>> messageReaders = Collections List<HttpMessageReader<?>> messageReaders = Collections
.singleton(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true))); .singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true)));
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders::stream); when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders);
Flux<String> resultFlux = defaultClientResponse.bodyToFlux(String.class); Flux<String> resultFlux = defaultClientResponse.bodyToFlux(String.class);
Mono<List<String>> result = resultFlux.collectList(); Mono<List<String>> result = resultFlux.collectList();

View File

@ -16,8 +16,6 @@
package org.springframework.web.reactive.function.client; package org.springframework.web.reactive.function.client;
import java.util.Optional;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -30,15 +28,15 @@ public class ExchangeStrategiesTests {
@Test @Test
public void empty() { public void empty() {
ExchangeStrategies strategies = ExchangeStrategies.empty().build(); ExchangeStrategies strategies = ExchangeStrategies.empty().build();
assertEquals(Optional.empty(), strategies.messageReaders().get().findFirst()); assertTrue(strategies.messageReaders().isEmpty());
assertEquals(Optional.empty(), strategies.messageWriters().get().findFirst()); assertTrue(strategies.messageWriters().isEmpty());
} }
@Test @Test
public void withDefaults() { public void withDefaults() {
ExchangeStrategies strategies = ExchangeStrategies.withDefaults(); ExchangeStrategies strategies = ExchangeStrategies.withDefaults();
assertNotEquals(Optional.empty(), strategies.messageReaders().get().findFirst()); assertFalse(strategies.messageReaders().isEmpty());
assertNotEquals(Optional.empty(), strategies.messageWriters().get().findFirst()); assertFalse(strategies.messageWriters().isEmpty());
} }
} }

View File

@ -23,8 +23,6 @@ import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.Test; import org.junit.Test;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
@ -196,13 +194,13 @@ public class DefaultEntityResponseBuilderTests {
ServerResponse.Context context = new ServerResponse.Context() { ServerResponse.Context context = new ServerResponse.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return Collections.<HttpMessageWriter<?>>singletonList(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()))::stream; return Collections.<HttpMessageWriter<?>>singletonList(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));
} }
@Override @Override
public Supplier<Stream<ViewResolver>> viewResolvers() { public List<ViewResolver> viewResolvers() {
return Collections.<ViewResolver>emptyList()::stream; return Collections.<ViewResolver>emptyList();
} }
}; };
HandlerStrategies strategies = HandlerStrategies.empty() HandlerStrategies strategies = HandlerStrategies.empty()

View File

@ -33,8 +33,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
import org.springframework.web.reactive.result.view.View; import org.springframework.web.reactive.result.view.View;
import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.reactive.result.view.ViewResolver;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.*;
import static org.mockito.Mockito.when;
/** /**
* @author Arjen Poutsma * @author Arjen Poutsma
@ -119,7 +118,7 @@ public class DefaultRenderingResponseTests {
viewResolvers.add(viewResolver); viewResolvers.add(viewResolver);
HandlerStrategies mockConfig = mock(HandlerStrategies.class); HandlerStrategies mockConfig = mock(HandlerStrategies.class);
when(mockConfig.viewResolvers()).thenReturn(viewResolvers::stream); when(mockConfig.viewResolvers()).thenReturn(viewResolvers);
StepVerifier.create(result) StepVerifier.create(result)
.expectNextMatches(response -> "view".equals(response.name()) && .expectNextMatches(response -> "view".equals(response.name()) &&

View File

@ -26,8 +26,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -66,7 +64,7 @@ public class DefaultServerRequestTests {
private ServerWebExchange mockExchange; private ServerWebExchange mockExchange;
Supplier<Stream<HttpMessageReader<?>>> messageReaders; List<HttpMessageReader<?>> messageReaders;
private DefaultServerRequest defaultRequest; private DefaultServerRequest defaultRequest;
@ -80,7 +78,7 @@ public class DefaultServerRequestTests {
when(mockExchange.getRequest()).thenReturn(mockRequest); when(mockExchange.getRequest()).thenReturn(mockRequest);
when(mockExchange.getResponse()).thenReturn(mockResponse); when(mockExchange.getResponse()).thenReturn(mockResponse);
this.messageReaders = Collections.<HttpMessageReader<?>>singleton(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true)))::stream; this.messageReaders = Collections.<HttpMessageReader<?>>singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(true)));
defaultRequest = new DefaultServerRequest(mockExchange, messageReaders); defaultRequest = new DefaultServerRequest(mockExchange, messageReaders);
} }
@ -238,7 +236,7 @@ public class DefaultServerRequestTests {
when(mockRequest.getHeaders()).thenReturn(httpHeaders); when(mockRequest.getHeaders()).thenReturn(httpHeaders);
when(mockRequest.getBody()).thenReturn(body); when(mockRequest.getBody()).thenReturn(body);
this.messageReaders = Collections.<HttpMessageReader<?>>emptySet()::stream; this.messageReaders = Collections.emptyList();
this.defaultRequest = new DefaultServerRequest(mockExchange, messageReaders); this.defaultRequest = new DefaultServerRequest(mockExchange, messageReaders);
Flux<String> resultFlux = defaultRequest.bodyToFlux(String.class); Flux<String> resultFlux = defaultRequest.bodyToFlux(String.class);

View File

@ -16,8 +16,6 @@
package org.springframework.web.reactive.function.server; package org.springframework.web.reactive.function.server;
import java.util.Optional;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -30,17 +28,17 @@ public class HandlerStrategiesTests {
@Test @Test
public void empty() { public void empty() {
HandlerStrategies strategies = HandlerStrategies.empty().build(); HandlerStrategies strategies = HandlerStrategies.empty().build();
assertEquals(Optional.empty(), strategies.messageReaders().get().findFirst()); assertTrue(strategies.messageReaders().isEmpty());
assertEquals(Optional.empty(), strategies.messageWriters().get().findFirst()); assertTrue(strategies.messageWriters().isEmpty());
assertEquals(Optional.empty(), strategies.viewResolvers().get().findFirst()); assertTrue(strategies.viewResolvers().isEmpty());
} }
@Test @Test
public void withDefaults() { public void withDefaults() {
HandlerStrategies strategies = HandlerStrategies.withDefaults(); HandlerStrategies strategies = HandlerStrategies.withDefaults();
assertNotEquals(Optional.empty(), strategies.messageReaders().get().findFirst()); assertFalse(strategies.messageReaders().isEmpty());
assertNotEquals(Optional.empty(), strategies.messageWriters().get().findFirst()); assertFalse(strategies.messageWriters().isEmpty());
assertEquals(Optional.empty(), strategies.viewResolvers().get().findFirst()); assertTrue(strategies.viewResolvers().isEmpty());
} }
} }

View File

@ -19,8 +19,7 @@ package org.springframework.web.reactive.function.server;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.function.Supplier; import java.util.List;
import java.util.stream.Stream;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -56,12 +55,12 @@ public class ResourceHandlerFunctionTests {
HandlerStrategies strategies = HandlerStrategies.withDefaults(); HandlerStrategies strategies = HandlerStrategies.withDefaults();
context = new ServerResponse.Context() { context = new ServerResponse.Context() {
@Override @Override
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() { public List<HttpMessageWriter<?>> messageWriters() {
return strategies.messageWriters(); return strategies.messageWriters();
} }
@Override @Override
public Supplier<Stream<ViewResolver>> viewResolvers() { public List<ViewResolver> viewResolvers() {
return strategies.viewResolvers(); return strategies.viewResolvers();
} }
}; };