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 * Set the character set to use for part headers such as
* "Content-Disposition" (and its filename parameter). * "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.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Decoder; 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.EncoderHttpMessageWriter;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -85,12 +85,22 @@ class BaseCodecConfigurer implements CodecConfigurer {
@Override @Override
public List<HttpMessageWriter<?>> getWriters() { 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<>(); List<HttpMessageWriter<?>> result = new ArrayList<>();
result.addAll(this.defaultCodecs.getTypedWriters()); result.addAll(this.defaultCodecs.getTypedWriters(forMultipart));
result.addAll(this.customCodecs.getTypedWriters()); result.addAll(this.customCodecs.getTypedWriters());
result.addAll(this.defaultCodecs.getObjectWriters()); result.addAll(this.defaultCodecs.getObjectWriters(forMultipart));
result.addAll(this.customCodecs.getObjectWriters()); result.addAll(this.customCodecs.getObjectWriters());
result.addAll(this.defaultCodecs.getCatchAllWriters()); 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}. * 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() { final List<HttpMessageReader<?>> getTypedReaders() {
if (!this.registerDefaults) { if (!this.registerDefaults) {
return Collections.emptyList(); return Collections.emptyList();
@ -112,9 +113,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return readers; return readers;
} }
/**
* Hook for client or server specific typed readers.
*/
protected void extendTypedReaders(List<HttpMessageReader<?>> typedReaders) { protected void extendTypedReaders(List<HttpMessageReader<?>> typedReaders) {
} }
/**
* Return Object readers (JSON, XML, SSE).
*/
final List<HttpMessageReader<?>> getObjectReaders() { final List<HttpMessageReader<?>> getObjectReaders() {
if (!this.registerDefaults) { if (!this.registerDefaults) {
return Collections.emptyList(); return Collections.emptyList();
@ -133,9 +140,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return readers; return readers;
} }
/**
* Hook for client or server specific Object readers.
*/
protected void extendObjectReaders(List<HttpMessageReader<?>> objectReaders) { protected void extendObjectReaders(List<HttpMessageReader<?>> objectReaders) {
} }
/**
* Return readers that need to be at the end, after all others.
*/
final List<HttpMessageReader<?>> getCatchAllReaders() { final List<HttpMessageReader<?>> getCatchAllReaders() {
if (!this.registerDefaults) { if (!this.registerDefaults) {
return Collections.emptyList(); return Collections.emptyList();
@ -145,10 +158,13 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return result; return result;
} }
/**
// Package private access to the configured writers... * Return writers that support specific types.
* @param forMultipart whether to returns writers for general use ("false"),
final List<HttpMessageWriter<?>> getTypedWriters() { * 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) { if (!this.registerDefaults) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -158,14 +174,26 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder())); writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder()));
writers.add(new ResourceHttpMessageWriter()); writers.add(new ResourceHttpMessageWriter());
writers.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly())); writers.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));
extendTypedWriters(writers); // No client or server specific multipart writers currently..
if (!forMultipart) {
extendTypedWriters(writers);
}
return writers; return writers;
} }
/**
* Hook for client or server specific typed writers.
*/
protected void extendTypedWriters(List<HttpMessageWriter<?>> typedWriters) { 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) { if (!this.registerDefaults) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -179,13 +207,22 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
if (jaxb2Present) { if (jaxb2Present) {
writers.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder())); writers.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
} }
extendObjectWriters(writers); // No client or server specific multipart writers currently..
if (!forMultipart) {
extendObjectWriters(writers);
}
return writers; return writers;
} }
/**
* Hook for client or server specific Object writers.
*/
protected void extendObjectWriters(List<HttpMessageWriter<?>> objectWriters) { protected void extendObjectWriters(List<HttpMessageWriter<?>> objectWriters) {
} }
/**
* Return writers that need to be at the end, after all others.
*/
List<HttpMessageWriter<?>> getCatchAllWriters() { List<HttpMessageWriter<?>> getCatchAllWriters() {
if (!this.registerDefaults) { if (!this.registerDefaults) {
return Collections.emptyList(); 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.ServerSentEventHttpMessageReader;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/** /**
* Default implementation of {@link ClientCodecConfigurer.ClientDefaultCodecs}. * Default implementation of {@link ClientCodecConfigurer.ClientDefaultCodecs}.
@ -45,18 +44,17 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
private Decoder<?> sseDecoder; private Decoder<?> sseDecoder;
@Nullable @Nullable
private Supplier<List<HttpMessageWriter<?>>> customTypedWriters; private Supplier<List<HttpMessageWriter<?>>> partWritersSupplier;
@Nullable
private Supplier<List<HttpMessageWriter<?>>> customObjectWriters;
void initCustomTypedWriters(Supplier<List<HttpMessageWriter<?>>> supplier) { /**
this.customTypedWriters = 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
void initCustomObjectWriters(Supplier<List<HttpMessageWriter<?>>> supplier) { * writer itself.
this.customObjectWriters = supplier; */
void setPartWritersSupplier(Supplier<List<HttpMessageWriter<?>>> supplier) {
this.partWritersSupplier = supplier;
} }
@ -86,36 +84,19 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
@Override @Override
protected void extendTypedWriters(List<HttpMessageWriter<?>> typedWriters) { protected void extendTypedWriters(List<HttpMessageWriter<?>> typedWriters) {
typedWriters.add(new MultipartHttpMessageWriter(getPartWriters(), new FormHttpMessageWriter()));
MultipartHttpMessageWriter multipartWriter = new MultipartHttpMessageWriter(
resolvePartWriters(typedWriters, getObjectWriters()), new FormHttpMessageWriter());
typedWriters.add(multipartWriter);
} }
private List<HttpMessageWriter<?>> resolvePartWriters(List<HttpMessageWriter<?>> typedWriters, @SuppressWarnings("ConstantConditions")
List<HttpMessageWriter<?>> objectWriters) { private List<HttpMessageWriter<?>> getPartWriters() {
return this.multipartCodecs != null ?
List<HttpMessageWriter<?>> partWriters; this.multipartCodecs.getWriters() : this.partWritersSupplier.get();
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;
} }
/**
* Default implementation of {@link ClientCodecConfigurer.MultipartCodecs}.
*/
private static class DefaultMultipartCodecs implements ClientCodecConfigurer.MultipartCodecs { private static class DefaultMultipartCodecs implements ClientCodecConfigurer.MultipartCodecs {
private final List<HttpMessageWriter<?>> writers = new ArrayList<>(); private final List<HttpMessageWriter<?>> writers = new ArrayList<>();
@ -137,4 +118,5 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo
return this.writers; return this.writers;
} }
} }
} }

View File

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

View File

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