Skip to content

Commit 802d083

Browse files
committed
Add register methods to CodecConfigurer.CustomCodecs
The new register methods replace the now deprecated encoder, decoder, reader, and writer methods, and also offer a choice to opt into default properties such maxInMemorySize, if configured. Backport of 11e321b See gh-24201
1 parent f5b43a2 commit 802d083

File tree

7 files changed

+221
-67
lines changed

7 files changed

+221
-67
lines changed

spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,34 +192,88 @@ interface DefaultCodecs {
192192
*/
193193
interface CustomCodecs {
194194

195+
/**
196+
* Register a custom codec. This is expected to be one of the following:
197+
* <ul>
198+
* <li>{@link HttpMessageReader}
199+
* <li>{@link HttpMessageWriter}
200+
* <li>{@link Encoder} (wrapped internally with {@link EncoderHttpMessageWriter})
201+
* <li>{@link Decoder} (wrapped internally with {@link DecoderHttpMessageReader})
202+
* </ul>
203+
* @param codec the codec to register
204+
* @since 5.1.13
205+
*/
206+
void register(Object codec);
207+
208+
/**
209+
* Variant of {@link #register(Object)} that also applies the below
210+
* properties, if configured, via {@link #defaultCodecs()}:
211+
* <ul>
212+
* <li>{@link CodecConfigurer.DefaultCodecs#maxInMemorySize(int) maxInMemorySize}
213+
* <li>{@link CodecConfigurer.DefaultCodecs#enableLoggingRequestDetails(boolean) enableLoggingRequestDetails}
214+
* </ul>
215+
* <p>The properties are applied every time {@link #getReaders()} or
216+
* {@link #getWriters()} are used to obtain the list of configured
217+
* readers or writers.
218+
* @param codec the codec to register and apply default config to
219+
* @since 5.1.13
220+
*/
221+
void registerWithDefaultConfig(Object codec);
222+
223+
/**
224+
* Variant of {@link #register(Object)} that also allows the caller to
225+
* apply the properties from {@link DefaultCodecConfig} to the given
226+
* codec. If you want to apply all the properties, prefer using
227+
* {@link #registerWithDefaultConfig(Object)}.
228+
* <p>The consumer is called every time {@link #getReaders()} or
229+
* {@link #getWriters()} are used to obtain the list of configured
230+
* readers or writers.
231+
* @param codec the codec to register
232+
* @param configConsumer consumer of the default config
233+
* @since 5.1.13
234+
*/
235+
void registerWithDefaultConfig(Object codec, Consumer<DefaultCodecConfig> configConsumer);
236+
195237
/**
196238
* Add a custom {@code Decoder} internally wrapped with
197239
* {@link DecoderHttpMessageReader}).
198240
* @param decoder the decoder to add
241+
* @deprecated as of 5.1.13, use {@link #register(Object)} or
242+
* {@link #registerWithDefaultConfig(Object)} instead.
199243
*/
244+
@Deprecated
200245
void decoder(Decoder<?> decoder);
201246

202247
/**
203248
* Add a custom {@code Encoder}, internally wrapped with
204249
* {@link EncoderHttpMessageWriter}.
205250
* @param encoder the encoder to add
251+
* @deprecated as of 5.1.13, use {@link #register(Object)} or
252+
* {@link #registerWithDefaultConfig(Object)} instead.
206253
*/
254+
@Deprecated
207255
void encoder(Encoder<?> encoder);
208256

209257
/**
210258
* Add a custom {@link HttpMessageReader}. For readers of type
211259
* {@link DecoderHttpMessageReader} consider using the shortcut
212260
* {@link #decoder(Decoder)} instead.
213261
* @param reader the reader to add
262+
* @deprecated as of 5.1.13, use {@link #register(Object)} or
263+
* {@link #registerWithDefaultConfig(Object)} instead.
214264
*/
265+
@Deprecated
215266
void reader(HttpMessageReader<?> reader);
216267

217268
/**
218269
* Add a custom {@link HttpMessageWriter}. For writers of type
219270
* {@link EncoderHttpMessageWriter} consider using the shortcut
220271
* {@link #encoder(Encoder)} instead.
221272
* @param writer the writer to add
273+
* @deprecated as of 5.1.13, use {@link #register(Object)} or
274+
* {@link #registerWithDefaultConfig(Object)} instead.
222275
*/
276+
@Deprecated
223277
void writer(HttpMessageWriter<?> writer);
224278

225279
/**
@@ -228,16 +282,21 @@ interface CustomCodecs {
228282
* guidelines applied to default ones, such as logging details and limiting
229283
* the amount of buffered data.
230284
* @param codecsConfigConsumer the default codecs configuration callback
231-
* @since 5.1.12
285+
* @deprecated as of 5.1.13, use {@link #registerWithDefaultConfig(Object)}
286+
* or {@link #registerWithDefaultConfig(Object, Consumer)} instead.
232287
*/
288+
@Deprecated
233289
void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer);
234290
}
235291

236292

237293
/**
238-
* Common options applied to default codecs and passed in a callback to custom codecs
239-
* so they get a chance to align their behavior on the default ones.
294+
* Exposes the values of properties configured through
295+
* {@link #defaultCodecs()} that are applied to default codecs.
296+
* The main purpose of this interface is to provide access to them so they
297+
* can also be applied to custom codecs if needed.
240298
* @since 5.1.12
299+
* @see CustomCodecs#registerWithDefaultConfig(Object, Consumer)
241300
*/
242301
interface DefaultCodecConfig {
243302

spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package org.springframework.http.codec.support;
1818

1919
import java.util.ArrayList;
20+
import java.util.LinkedHashMap;
2021
import java.util.List;
22+
import java.util.Map;
2123
import java.util.function.Consumer;
2224

2325
import org.springframework.core.ResolvableType;
@@ -40,8 +42,6 @@
4042
*/
4143
abstract class BaseCodecConfigurer implements CodecConfigurer {
4244

43-
protected boolean customCodecsInitialized;
44-
4545
protected final BaseDefaultCodecs defaultCodecs;
4646

4747
protected final DefaultCustomCodecs customCodecs;
@@ -91,21 +91,20 @@ public CustomCodecs customCodecs() {
9191

9292
@Override
9393
public List<HttpMessageReader<?>> getReaders() {
94-
initializeCustomCodecs();
95-
List<HttpMessageReader<?>> result = new ArrayList<>();
94+
this.defaultCodecs.applyDefaultConfig(this.customCodecs);
9695

96+
List<HttpMessageReader<?>> result = new ArrayList<>();
9797
result.addAll(this.defaultCodecs.getTypedReaders());
98-
result.addAll(this.customCodecs.getTypedReaders());
99-
98+
result.addAll(this.customCodecs.getTypedReaders().keySet());
10099
result.addAll(this.defaultCodecs.getObjectReaders());
101-
result.addAll(this.customCodecs.getObjectReaders());
102-
100+
result.addAll(this.customCodecs.getObjectReaders().keySet());
103101
result.addAll(this.defaultCodecs.getCatchAllReaders());
104102
return result;
105103
}
106104

107105
@Override
108106
public List<HttpMessageWriter<?>> getWriters() {
107+
this.defaultCodecs.applyDefaultConfig(this.customCodecs);
109108
return getWritersInternal(false);
110109
}
111110

@@ -117,14 +116,13 @@ public List<HttpMessageWriter<?>> getWriters() {
117116
* same except for the multipart writer itself.
118117
*/
119118
protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
120-
initializeCustomCodecs();
121119
List<HttpMessageWriter<?>> result = new ArrayList<>();
122120

123121
result.addAll(this.defaultCodecs.getTypedWriters(forMultipart));
124-
result.addAll(this.customCodecs.getTypedWriters());
122+
result.addAll(this.customCodecs.getTypedWriters().keySet());
125123

126124
result.addAll(this.defaultCodecs.getObjectWriters(forMultipart));
127-
result.addAll(this.customCodecs.getObjectWriters());
125+
result.addAll(this.customCodecs.getObjectWriters().keySet());
128126

129127
result.addAll(this.defaultCodecs.getCatchAllWriters());
130128
return result;
@@ -133,28 +131,21 @@ protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
133131
@Override
134132
public abstract CodecConfigurer clone();
135133

136-
private void initializeCustomCodecs() {
137-
if(!this.customCodecsInitialized) {
138-
this.customCodecs.configConsumers.forEach(consumer -> consumer.accept(this.defaultCodecs));
139-
this.customCodecsInitialized = true;
140-
}
141-
}
142-
143134

144135
/**
145136
* Default implementation of {@code CustomCodecs}.
146137
*/
147138
protected static final class DefaultCustomCodecs implements CustomCodecs {
148139

149-
private final List<HttpMessageReader<?>> typedReaders = new ArrayList<>();
140+
private final Map<HttpMessageReader<?>, Boolean> typedReaders = new LinkedHashMap<>(4);
150141

151-
private final List<HttpMessageWriter<?>> typedWriters = new ArrayList<>();
142+
private final Map<HttpMessageWriter<?>, Boolean> typedWriters = new LinkedHashMap<>(4);
152143

153-
private final List<HttpMessageReader<?>> objectReaders = new ArrayList<>();
144+
private final Map<HttpMessageReader<?>, Boolean> objectReaders = new LinkedHashMap<>(4);
154145

155-
private final List<HttpMessageWriter<?>> objectWriters = new ArrayList<>();
146+
private final Map<HttpMessageWriter<?>, Boolean> objectWriters = new LinkedHashMap<>(4);
156147

157-
private final List<Consumer<DefaultCodecConfig>> configConsumers = new ArrayList<>();
148+
private final List<Consumer<DefaultCodecConfig>> defaultConfigConsumers = new ArrayList<>(4);
158149

159150
DefaultCustomCodecs() {
160151
}
@@ -164,56 +155,103 @@ protected static final class DefaultCustomCodecs implements CustomCodecs {
164155
* @since 5.1.12
165156
*/
166157
DefaultCustomCodecs(DefaultCustomCodecs other) {
167-
other.typedReaders.addAll(this.typedReaders);
168-
other.typedWriters.addAll(this.typedWriters);
169-
other.objectReaders.addAll(this.objectReaders);
170-
other.objectWriters.addAll(this.objectWriters);
158+
other.typedReaders.putAll(this.typedReaders);
159+
other.typedWriters.putAll(this.typedWriters);
160+
other.objectReaders.putAll(this.objectReaders);
161+
other.objectWriters.putAll(this.objectWriters);
162+
}
163+
164+
@Override
165+
public void register(Object codec) {
166+
addCodec(codec, false);
171167
}
172168

169+
@Override
170+
public void registerWithDefaultConfig(Object codec) {
171+
addCodec(codec, true);
172+
}
173+
174+
@Override
175+
public void registerWithDefaultConfig(Object codec, Consumer<DefaultCodecConfig> configConsumer) {
176+
addCodec(codec, false);
177+
this.defaultConfigConsumers.add(configConsumer);
178+
}
179+
180+
@SuppressWarnings("deprecation")
173181
@Override
174182
public void decoder(Decoder<?> decoder) {
175-
reader(new DecoderHttpMessageReader<>(decoder));
183+
addCodec(decoder, false);
176184
}
177185

186+
@SuppressWarnings("deprecation")
178187
@Override
179188
public void encoder(Encoder<?> encoder) {
180-
writer(new EncoderHttpMessageWriter<>(encoder));
189+
addCodec(encoder, false);
181190
}
182191

192+
@SuppressWarnings("deprecation")
183193
@Override
184194
public void reader(HttpMessageReader<?> reader) {
185-
boolean canReadToObject = reader.canRead(ResolvableType.forClass(Object.class), null);
186-
(canReadToObject ? this.objectReaders : this.typedReaders).add(reader);
195+
addCodec(reader, false);
187196
}
188197

198+
@SuppressWarnings("deprecation")
189199
@Override
190200
public void writer(HttpMessageWriter<?> writer) {
191-
boolean canWriteObject = writer.canWrite(ResolvableType.forClass(Object.class), null);
192-
(canWriteObject ? this.objectWriters : this.typedWriters).add(writer);
201+
addCodec(writer, false);
193202
}
194203

204+
@SuppressWarnings("deprecation")
195205
@Override
196206
public void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer) {
197-
this.configConsumers.add(codecsConfigConsumer);
207+
this.defaultConfigConsumers.add(codecsConfigConsumer);
208+
}
209+
210+
private void addCodec(Object codec, boolean applyDefaultConfig) {
211+
212+
if (codec instanceof Decoder) {
213+
codec = new DecoderHttpMessageReader<>((Decoder<?>) codec);
214+
}
215+
else if (codec instanceof Encoder) {
216+
codec = new EncoderHttpMessageWriter<>((Encoder<?>) codec);
217+
}
218+
219+
if (codec instanceof HttpMessageReader) {
220+
HttpMessageReader<?> reader = (HttpMessageReader<?>) codec;
221+
boolean canReadToObject = reader.canRead(ResolvableType.forClass(Object.class), null);
222+
(canReadToObject ? this.objectReaders : this.typedReaders).put(reader, applyDefaultConfig);
223+
}
224+
else if (codec instanceof HttpMessageWriter) {
225+
HttpMessageWriter<?> writer = (HttpMessageWriter<?>) codec;
226+
boolean canWriteObject = writer.canWrite(ResolvableType.forClass(Object.class), null);
227+
(canWriteObject ? this.objectWriters : this.typedWriters).put(writer, applyDefaultConfig);
228+
}
229+
else {
230+
throw new IllegalArgumentException("Unexpected codec type: " + codec.getClass().getName());
231+
}
198232
}
199233

200234
// Package private accessors...
201235

202-
List<HttpMessageReader<?>> getTypedReaders() {
236+
Map<HttpMessageReader<?>, Boolean> getTypedReaders() {
203237
return this.typedReaders;
204238
}
205239

206-
List<HttpMessageWriter<?>> getTypedWriters() {
240+
Map<HttpMessageWriter<?>, Boolean> getTypedWriters() {
207241
return this.typedWriters;
208242
}
209243

210-
List<HttpMessageReader<?>> getObjectReaders() {
244+
Map<HttpMessageReader<?>, Boolean> getObjectReaders() {
211245
return this.objectReaders;
212246
}
213247

214-
List<HttpMessageWriter<?>> getObjectWriters() {
248+
Map<HttpMessageWriter<?>, Boolean> getObjectWriters() {
215249
return this.objectWriters;
216250
}
251+
252+
List<Consumer<DefaultCodecConfig>> getDefaultConfigConsumers() {
253+
return this.defaultConfigConsumers;
254+
}
217255
}
218256

219257
}

spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Collections;
2121
import java.util.List;
22+
import java.util.Map;
2223

2324
import org.springframework.core.codec.AbstractDataBufferDecoder;
2425
import org.springframework.core.codec.ByteArrayDecoder;
@@ -432,6 +433,21 @@ List<HttpMessageWriter<?>> getCatchAllWriters() {
432433
return result;
433434
}
434435

436+
void applyDefaultConfig(BaseCodecConfigurer.DefaultCustomCodecs customCodecs) {
437+
applyDefaultConfig(customCodecs.getTypedReaders());
438+
applyDefaultConfig(customCodecs.getObjectReaders());
439+
applyDefaultConfig(customCodecs.getTypedWriters());
440+
applyDefaultConfig(customCodecs.getObjectWriters());
441+
customCodecs.getDefaultConfigConsumers().forEach(consumer -> consumer.accept(this));
442+
}
443+
444+
private void applyDefaultConfig(Map<?, Boolean> readers) {
445+
readers.entrySet().stream()
446+
.filter(Map.Entry::getValue)
447+
.map(Map.Entry::getKey)
448+
.forEach(this::initCodec);
449+
}
450+
435451

436452
// Accessors for use in subclasses...
437453

0 commit comments

Comments
 (0)