CodecConfigurer internal refactoring

Improve how HTTP message writers are obtained for general use vs for
multipart requests.
This commit is contained in:
Rossen Stoyanchev 2018-05-21 18:50:00 -04:00
parent 6e5273f08e
commit 9b496b1264
6 changed files with 91 additions and 63 deletions

View File

@ -134,6 +134,14 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter<MultiValueM
}
/**
* Return the configured part writers.
* @since 5.0.7
*/
public List<HttpMessageWriter<?>> getPartWriters() {
return Collections.unmodifiableList(partWriters);
}
/**
* Set the character set to use for part headers such as
* "Content-Disposition" (and its filename parameter).

View File

@ -18,7 +18,6 @@ package org.springframework.http.codec.support;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Decoder;
@ -28,6 +27,7 @@ import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.EncoderHttpMessageWriter;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.util.Assert;
/**
@ -85,12 +85,22 @@ class BaseCodecConfigurer implements CodecConfigurer {
@Override
public List<HttpMessageWriter<?>> getWriters() {
return getWritersInternal(false);
}
/**
* Internal method that returns the configured writers.
* @param forMultipart whether to returns writers for general use ("false"),
* or for multipart requests only ("true"). Generally the two sets are the
* same except for the multipart writer itself.
*/
protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
List<HttpMessageWriter<?>> result = new ArrayList<>();
result.addAll(this.defaultCodecs.getTypedWriters());
result.addAll(this.defaultCodecs.getTypedWriters(forMultipart));
result.addAll(this.customCodecs.getTypedWriters());
result.addAll(this.defaultCodecs.getObjectWriters());
result.addAll(this.defaultCodecs.getObjectWriters(forMultipart));
result.addAll(this.customCodecs.getObjectWriters());
result.addAll(this.defaultCodecs.getCatchAllWriters());
@ -98,17 +108,6 @@ class BaseCodecConfigurer implements CodecConfigurer {
}
// Accessors for use in sub-classes...
protected Supplier<List<HttpMessageWriter<?>>> getCustomTypedWriters() {
return () -> this.customCodecs.typedWriters;
}
protected Supplier<List<HttpMessageWriter<?>>> getCustomObjectWriters() {
return () -> this.customCodecs.objectWriters;
}
/**
* Default implementation of {@code CustomCodecs}.
*/

View File

@ -95,8 +95,9 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
}
// Package private access to the configured readers...
/**
* Return readers that support specific types.
*/
final List<HttpMessageReader<?>> getTypedReaders() {
if (!this.registerDefaults) {
return Collections.emptyList();
@ -112,9 +113,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return readers;
}
/**
* Hook for client or server specific typed readers.
*/
protected void extendTypedReaders(List<HttpMessageReader<?>> typedReaders) {
}
/**
* Return Object readers (JSON, XML, SSE).
*/
final List<HttpMessageReader<?>> getObjectReaders() {
if (!this.registerDefaults) {
return Collections.emptyList();
@ -133,9 +140,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return readers;
}
/**
* Hook for client or server specific Object readers.
*/
protected void extendObjectReaders(List<HttpMessageReader<?>> objectReaders) {
}
/**
* Return readers that need to be at the end, after all others.
*/
final List<HttpMessageReader<?>> getCatchAllReaders() {
if (!this.registerDefaults) {
return Collections.emptyList();
@ -145,10 +158,13 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return result;
}
// Package private access to the configured writers...
final List<HttpMessageWriter<?>> getTypedWriters() {
/**
* Return writers that support specific types.
* @param forMultipart whether to returns writers for general use ("false"),
* or for multipart requests only ("true"). Generally the two sets are the
* same except for the multipart writer itself.
*/
final List<HttpMessageWriter<?>> getTypedWriters(boolean forMultipart) {
if (!this.registerDefaults) {
return Collections.emptyList();
}
@ -158,14 +174,26 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder()));
writers.add(new ResourceHttpMessageWriter());
writers.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
extendTypedWriters(writers);
// No client or server specific multipart writers currently..
if (!forMultipart) {
extendTypedWriters(writers);
}
return writers;
}
/**
* Hook for client or server specific typed writers.
*/
protected void extendTypedWriters(List<HttpMessageWriter<?>> typedWriters) {
}
final List<HttpMessageWriter<?>> getObjectWriters() {
/**
* Return Object writers (JSON, XML, SSE).
* @param forMultipart whether to returns writers for general use ("false"),
* or for multipart requests only ("true"). Generally the two sets are the
* same except for the multipart writer itself.
*/
final List<HttpMessageWriter<?>> getObjectWriters(boolean forMultipart) {
if (!this.registerDefaults) {
return Collections.emptyList();
}
@ -179,13 +207,22 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
if (jaxb2Present) {
writers.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
}
extendObjectWriters(writers);
// No client or server specific multipart writers currently..
if (!forMultipart) {
extendObjectWriters(writers);
}
return writers;
}
/**
* Hook for client or server specific Object writers.
*/
protected void extendObjectWriters(List<HttpMessageWriter<?>> objectWriters) {
}
/**
* Return writers that need to be at the end, after all others.
*/
List<HttpMessageWriter<?>> getCatchAllWriters() {
if (!this.registerDefaults) {
return Collections.emptyList();

View File

@ -29,7 +29,6 @@ import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default implementation of {@link ClientCodecConfigurer.ClientDefaultCodecs}.
@ -45,18 +44,17 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
private Decoder<?> sseDecoder;
@Nullable
private Supplier<List<HttpMessageWriter<?>>> customTypedWriters;
@Nullable
private Supplier<List<HttpMessageWriter<?>>> customObjectWriters;
private Supplier<List<HttpMessageWriter<?>>> partWritersSupplier;
void initCustomTypedWriters(Supplier<List<HttpMessageWriter<?>>> supplier) {
this.customTypedWriters = supplier;
}
void initCustomObjectWriters(Supplier<List<HttpMessageWriter<?>>> supplier) {
this.customObjectWriters = supplier;
/**
* Set a supplier for part writers to use when
* {@link #multipartCodecs()} are not explicitly configured.
* That's the same set of writers as for general except for the multipart
* writer itself.
*/
void setPartWritersSupplier(Supplier<List<HttpMessageWriter<?>>> supplier) {
this.partWritersSupplier = supplier;
}
@ -86,36 +84,19 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
@Override
protected void extendTypedWriters(List<HttpMessageWriter<?>> typedWriters) {
MultipartHttpMessageWriter multipartWriter = new MultipartHttpMessageWriter(
resolvePartWriters(typedWriters, getObjectWriters()), new FormHttpMessageWriter());
typedWriters.add(multipartWriter);
typedWriters.add(new MultipartHttpMessageWriter(getPartWriters(), new FormHttpMessageWriter()));
}
private List<HttpMessageWriter<?>> resolvePartWriters(List<HttpMessageWriter<?>> typedWriters,
List<HttpMessageWriter<?>> objectWriters) {
List<HttpMessageWriter<?>> partWriters;
if (this.multipartCodecs != null) {
partWriters = this.multipartCodecs.getWriters();
}
else {
Assert.notNull(this.customTypedWriters, "Expected custom typed writers supplier.");
Assert.notNull(this.customObjectWriters, "Expected custom object writers supplier.");
partWriters = new ArrayList<>(typedWriters);
partWriters.addAll(this.customTypedWriters.get());
partWriters.addAll(objectWriters);
partWriters.addAll(this.customObjectWriters.get());
partWriters.addAll(super.getCatchAllWriters());
}
return partWriters;
@SuppressWarnings("ConstantConditions")
private List<HttpMessageWriter<?>> getPartWriters() {
return this.multipartCodecs != null ?
this.multipartCodecs.getWriters() : this.partWritersSupplier.get();
}
/**
* Default implementation of {@link ClientCodecConfigurer.MultipartCodecs}.
*/
private static class DefaultMultipartCodecs implements ClientCodecConfigurer.MultipartCodecs {
private final List<HttpMessageWriter<?>> writers = new ArrayList<>();
@ -137,4 +118,5 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
return this.writers;
}
}
}

View File

@ -29,8 +29,7 @@ public class DefaultClientCodecConfigurer extends BaseCodecConfigurer implements
public DefaultClientCodecConfigurer() {
super(new ClientDefaultCodecsImpl());
((ClientDefaultCodecsImpl) defaultCodecs()).initCustomTypedWriters(getCustomTypedWriters());
((ClientDefaultCodecsImpl) defaultCodecs()).initCustomObjectWriters(getCustomObjectWriters());
((ClientDefaultCodecsImpl) defaultCodecs()).setPartWritersSupplier(() -> getWritersInternal(true));
}

View File

@ -37,6 +37,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.EncoderHttpMessageWriter;
import org.springframework.http.codec.FormHttpMessageReader;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ResourceHttpMessageWriter;
@ -66,12 +67,13 @@ public class CodecConfigurerTests {
@Test
public void defaultReaders() {
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
assertEquals(9, readers.size());
assertEquals(10, readers.size());
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ResourceDecoder.class, getNextDecoder(readers).getClass());
assertStringDecoder(getNextDecoder(readers), true);
assertEquals(FormHttpMessageReader.class, readers.get(this.index.getAndIncrement()).getClass());
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());
assertEquals(Jackson2SmileDecoder.class, getNextDecoder(readers).getClass());
assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass());
@ -115,12 +117,13 @@ public class CodecConfigurerTests {
List<HttpMessageReader<?>> readers = this.configurer.getReaders();
assertEquals(13, readers.size());
assertEquals(14, readers.size());
assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass());
assertEquals(ResourceDecoder.class, getNextDecoder(readers).getClass());
assertEquals(StringDecoder.class, getNextDecoder(readers).getClass());
assertEquals(FormHttpMessageReader.class, readers.get(this.index.getAndIncrement()).getClass());
assertSame(customDecoder1, getNextDecoder(readers));
assertSame(customReader1, readers.get(this.index.getAndIncrement()));
assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());