Skip to content

Commit d0f5701

Browse files
committed
Change default codecs ordering and add Jackson CBOR
This commit updates BaseDefaultCodecs by adding Kotlin Serialization codecs before their Jackson/GSON counterparts with their new default behavior that only handles classes with `@Serializable` at type or generics level. When there is no alternative codec for the same mime type, Kotlin Serialization codecs handle all supported cases. This commit also adds missing Jackson CBOR codecs, and moves both CBOR and Protobuf codecs to a lower priority, as they are less commonly used than JSON ones, with the same ordering used on Spring MVC side. See gh-35761 Closes gh-35787
1 parent 19dd488 commit d0f5701

File tree

6 files changed

+207
-66
lines changed

6 files changed

+207
-66
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ default void jackson2SmileEncoder(Encoder<?> encoder) {
208208
jacksonSmileEncoder(encoder);
209209
}
210210

211+
/**
212+
* Override the default Jackson 3.x CBOR {@code Decoder}.
213+
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be
214+
* applied to the given decoder.
215+
* @param decoder the decoder instance to use
216+
* @since 7.0
217+
* @see org.springframework.http.codec.cbor.JacksonCborDecoder
218+
*/
219+
void jacksonCborDecoder(Decoder<?> decoder);
220+
221+
/**
222+
* Override the default Jackson 3.x CBOR {@code Encoder}.
223+
* @param encoder the encoder instance to use
224+
* @since 7.0
225+
* @see org.springframework.http.codec.cbor.JacksonCborEncoder
226+
*/
227+
void jacksonCborEncoder(Encoder<?> encoder);
228+
211229
/**
212230
* Override the default Protobuf {@code Decoder}.
213231
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be

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

Lines changed: 124 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
import org.springframework.http.codec.ResourceHttpMessageWriter;
5252
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
5353
import org.springframework.http.codec.ServerSentEventHttpMessageWriter;
54+
import org.springframework.http.codec.cbor.Jackson2CborDecoder;
55+
import org.springframework.http.codec.cbor.Jackson2CborEncoder;
56+
import org.springframework.http.codec.cbor.JacksonCborDecoder;
57+
import org.springframework.http.codec.cbor.JacksonCborEncoder;
5458
import org.springframework.http.codec.cbor.KotlinSerializationCborDecoder;
5559
import org.springframework.http.codec.cbor.KotlinSerializationCborEncoder;
5660
import org.springframework.http.codec.json.AbstractJackson2Decoder;
@@ -101,6 +105,10 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
101105

102106
private static final boolean JACKSON_2_SMILE_PRESENT;
103107

108+
private static final boolean JACKSON_CBOR_PRESENT;
109+
110+
private static final boolean JACKSON_2_CBOR_PRESENT;
111+
104112
private static final boolean JAXB_2_PRESENT;
105113

106114
private static final boolean PROTOBUF_PRESENT;
@@ -121,6 +129,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
121129
GSON_PRESENT = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
122130
JACKSON_SMILE_PRESENT = JACKSON_PRESENT && ClassUtils.isPresent("tools.jackson.dataformat.smile.SmileMapper", classLoader);
123131
JACKSON_2_SMILE_PRESENT = JACKSON_2_PRESENT && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
132+
JACKSON_CBOR_PRESENT = JACKSON_PRESENT && ClassUtils.isPresent("tools.jackson.dataformat.cbor.CBORMapper", classLoader);
133+
JACKSON_2_CBOR_PRESENT = JACKSON_2_PRESENT && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper", classLoader);
124134
JAXB_2_PRESENT = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
125135
PROTOBUF_PRESENT = ClassUtils.isPresent("com.google.protobuf.Message", classLoader);
126136
NETTY_BYTE_BUF_PRESENT = ClassUtils.isPresent("io.netty.buffer.ByteBuf", classLoader);
@@ -142,6 +152,10 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
142152

143153
private @Nullable Decoder<?> jacksonSmileDecoder;
144154

155+
private @Nullable Encoder<?> jacksonCborEncoder;
156+
157+
private @Nullable Decoder<?> jacksonCborDecoder;
158+
145159
private @Nullable Decoder<?> protobufDecoder;
146160

147161
private @Nullable Encoder<?> protobufEncoder;
@@ -221,6 +235,8 @@ protected BaseDefaultCodecs(BaseDefaultCodecs other) {
221235
this.gsonEncoder = other.gsonEncoder;
222236
this.jacksonSmileDecoder = other.jacksonSmileDecoder;
223237
this.jacksonSmileEncoder = other.jacksonSmileEncoder;
238+
this.jacksonCborDecoder = other.jacksonCborDecoder;
239+
this.jacksonCborEncoder = other.jacksonCborEncoder;
224240
this.protobufDecoder = other.protobufDecoder;
225241
this.protobufEncoder = other.protobufEncoder;
226242
this.jaxb2Decoder = other.jaxb2Decoder;
@@ -283,6 +299,19 @@ public void jacksonSmileEncoder(Encoder<?> encoder) {
283299
initTypedWriters();
284300
}
285301

302+
@Override
303+
public void jacksonCborDecoder(Decoder<?> decoder) {
304+
this.jacksonCborDecoder = decoder;
305+
initObjectReaders();
306+
}
307+
308+
@Override
309+
public void jacksonCborEncoder(Encoder<?> encoder) {
310+
this.jacksonCborEncoder = encoder;
311+
initObjectWriters();
312+
initTypedWriters();
313+
}
314+
286315
@Override
287316
public void protobufDecoder(Decoder<?> decoder) {
288317
this.protobufDecoder = decoder;
@@ -604,33 +633,32 @@ protected void initObjectReaders() {
604633
if (!this.registerDefaults) {
605634
return;
606635
}
607-
if (KOTLIN_SERIALIZATION_CBOR_PRESENT) {
608-
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(this.kotlinSerializationCborDecoder != null ?
609-
(KotlinSerializationCborDecoder) this.kotlinSerializationCborDecoder :
610-
new KotlinSerializationCborDecoder()));
611-
}
612-
if (KOTLIN_SERIALIZATION_PROTOBUF_PRESENT) {
613-
addCodec(this.objectReaders,
614-
new DecoderHttpMessageReader<>(this.kotlinSerializationProtobufDecoder != null ?
615-
(KotlinSerializationProtobufDecoder) this.kotlinSerializationProtobufDecoder :
616-
new KotlinSerializationProtobufDecoder()));
636+
if (KOTLIN_SERIALIZATION_JSON_PRESENT) {
637+
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getKotlinSerializationJsonDecoder()));
617638
}
618639
if (JACKSON_PRESENT || JACKSON_2_PRESENT) {
619640
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getJacksonJsonDecoder()));
620641
}
621642
else if (GSON_PRESENT) {
622643
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getGsonDecoder()));
623644
}
624-
else if (KOTLIN_SERIALIZATION_JSON_PRESENT) {
625-
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getKotlinSerializationJsonDecoder()));
626-
}
627645
if (JACKSON_SMILE_PRESENT || JACKSON_2_SMILE_PRESENT) {
628646
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getJacksonSmileDecoder()));
629647
}
648+
if (KOTLIN_SERIALIZATION_CBOR_PRESENT) {
649+
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getKotlinSerializationCborDecoder()));
650+
}
651+
if (JACKSON_CBOR_PRESENT || JACKSON_2_CBOR_PRESENT) {
652+
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(getJacksonCborDecoder()));
653+
}
630654
if (JAXB_2_PRESENT) {
631655
addCodec(this.objectReaders, new DecoderHttpMessageReader<>(this.jaxb2Decoder != null ?
632656
(Jaxb2XmlDecoder) this.jaxb2Decoder : new Jaxb2XmlDecoder()));
633657
}
658+
if (KOTLIN_SERIALIZATION_PROTOBUF_PRESENT) {
659+
addCodec(this.objectReaders,
660+
new DecoderHttpMessageReader<>(getKotlinSerializationProtobufDecoder()));
661+
}
634662

635663
// client vs server..
636664
extendObjectReaders(this.objectReaders);
@@ -744,32 +772,31 @@ protected void initObjectWriters() {
744772
*/
745773
final List<HttpMessageWriter<?>> getBaseObjectWriters() {
746774
List<HttpMessageWriter<?>> writers = new ArrayList<>();
747-
if (KOTLIN_SERIALIZATION_CBOR_PRESENT) {
748-
addCodec(writers, new EncoderHttpMessageWriter<>(this.kotlinSerializationCborEncoder != null ?
749-
(KotlinSerializationCborEncoder) this.kotlinSerializationCborEncoder :
750-
new KotlinSerializationCborEncoder()));
751-
}
752-
if (KOTLIN_SERIALIZATION_PROTOBUF_PRESENT) {
753-
addCodec(writers, new EncoderHttpMessageWriter<>(this.kotlinSerializationProtobufEncoder != null ?
754-
(KotlinSerializationProtobufEncoder) this.kotlinSerializationProtobufEncoder :
755-
new KotlinSerializationProtobufEncoder()));
775+
if (KOTLIN_SERIALIZATION_JSON_PRESENT) {
776+
addCodec(writers, new EncoderHttpMessageWriter<>(getKotlinSerializationJsonEncoder()));
756777
}
757778
if (JACKSON_PRESENT || JACKSON_2_PRESENT) {
758779
addCodec(writers, new EncoderHttpMessageWriter<>(getJacksonJsonEncoder()));
759780
}
760781
else if (GSON_PRESENT) {
761782
addCodec(writers, new EncoderHttpMessageWriter<>(getGsonEncoder()));
762783
}
763-
else if (KOTLIN_SERIALIZATION_JSON_PRESENT) {
764-
addCodec(writers, new EncoderHttpMessageWriter<>(getKotlinSerializationJsonEncoder()));
765-
}
766784
if (JACKSON_SMILE_PRESENT || JACKSON_2_SMILE_PRESENT) {
767785
addCodec(writers, new EncoderHttpMessageWriter<>(getJacksonSmileEncoder()));
768786
}
787+
if (KOTLIN_SERIALIZATION_CBOR_PRESENT) {
788+
addCodec(writers, new EncoderHttpMessageWriter<>(getKotlinSerializationCborEncoder()));
789+
}
790+
if (JACKSON_CBOR_PRESENT || JACKSON_2_CBOR_PRESENT) {
791+
addCodec(writers, new EncoderHttpMessageWriter<>(getJacksonCborEncoder()));
792+
}
769793
if (JAXB_2_PRESENT) {
770794
addCodec(writers, new EncoderHttpMessageWriter<>(this.jaxb2Encoder != null ?
771795
(Jaxb2XmlEncoder) this.jaxb2Encoder : new Jaxb2XmlEncoder()));
772796
}
797+
if (KOTLIN_SERIALIZATION_PROTOBUF_PRESENT) {
798+
addCodec(writers, new EncoderHttpMessageWriter<>(getKotlinSerializationProtobufEncoder()));
799+
}
773800
return writers;
774801
}
775802

@@ -876,14 +903,14 @@ protected Encoder<?> getGsonEncoder() {
876903
@SuppressWarnings("removal")
877904
protected Decoder<?> getJacksonSmileDecoder() {
878905
if (this.jacksonSmileDecoder == null) {
879-
if (JACKSON_PRESENT) {
906+
if (JACKSON_SMILE_PRESENT) {
880907
this.jacksonSmileDecoder = new JacksonSmileDecoder();
881908
}
882-
else if (JACKSON_2_PRESENT) {
909+
else if (JACKSON_2_SMILE_PRESENT) {
883910
this.jacksonSmileDecoder = new Jackson2SmileDecoder();
884911
}
885912
else {
886-
throw new IllegalStateException("Jackson not present");
913+
throw new IllegalStateException("Jackson Smile support not present");
887914
}
888915
}
889916
return this.jacksonSmileDecoder;
@@ -892,33 +919,97 @@ else if (JACKSON_2_PRESENT) {
892919
@SuppressWarnings("removal")
893920
protected Encoder<?> getJacksonSmileEncoder() {
894921
if (this.jacksonSmileEncoder == null) {
895-
if (JACKSON_PRESENT) {
922+
if (JACKSON_SMILE_PRESENT) {
896923
this.jacksonSmileEncoder = new JacksonSmileEncoder();
897924
}
898-
else if (JACKSON_2_PRESENT) {
925+
else if (JACKSON_2_SMILE_PRESENT) {
899926
this.jacksonSmileEncoder = new Jackson2SmileEncoder();
900927
}
901928
else {
902-
throw new IllegalStateException("Jackson not present");
929+
throw new IllegalStateException("Jackson Smile support not present");
903930
}
904931
}
905932
return this.jacksonSmileEncoder;
906933
}
907934

935+
@SuppressWarnings("removal")
936+
protected Decoder<?> getJacksonCborDecoder() {
937+
if (this.jacksonCborDecoder == null) {
938+
if (JACKSON_CBOR_PRESENT) {
939+
this.jacksonCborDecoder = new JacksonCborDecoder();
940+
}
941+
else if (JACKSON_2_CBOR_PRESENT) {
942+
this.jacksonCborDecoder = new Jackson2CborDecoder();
943+
}
944+
else {
945+
throw new IllegalStateException("Jackson CBOR support not present");
946+
}
947+
}
948+
return this.jacksonCborDecoder;
949+
}
950+
951+
@SuppressWarnings("removal")
952+
protected Encoder<?> getJacksonCborEncoder() {
953+
if (this.jacksonCborEncoder == null) {
954+
if (JACKSON_CBOR_PRESENT) {
955+
this.jacksonCborEncoder = new JacksonCborEncoder();
956+
}
957+
else if (JACKSON_2_CBOR_PRESENT) {
958+
this.jacksonCborEncoder = new Jackson2CborEncoder();
959+
}
960+
else {
961+
throw new IllegalStateException("Jackson CBOR support not present");
962+
}
963+
}
964+
return this.jacksonCborEncoder;
965+
}
966+
908967
protected Decoder<?> getKotlinSerializationJsonDecoder() {
909968
if (this.kotlinSerializationJsonDecoder == null) {
910-
this.kotlinSerializationJsonDecoder = new KotlinSerializationJsonDecoder();
969+
this.kotlinSerializationJsonDecoder = (this.jacksonJsonDecoder != null || JACKSON_PRESENT || JACKSON_2_PRESENT || GSON_PRESENT ?
970+
new KotlinSerializationJsonDecoder() : new KotlinSerializationJsonDecoder(type -> true));
911971
}
912972
return this.kotlinSerializationJsonDecoder;
913973
}
914974

915975
protected Encoder<?> getKotlinSerializationJsonEncoder() {
916976
if (this.kotlinSerializationJsonEncoder == null) {
917-
this.kotlinSerializationJsonEncoder = new KotlinSerializationJsonEncoder();
977+
this.kotlinSerializationJsonEncoder = (this.jacksonJsonDecoder != null || JACKSON_PRESENT || JACKSON_2_PRESENT || GSON_PRESENT ?
978+
new KotlinSerializationJsonEncoder() : new KotlinSerializationJsonEncoder(type -> true));
918979
}
919980
return this.kotlinSerializationJsonEncoder;
920981
}
921982

983+
protected Decoder<?> getKotlinSerializationCborDecoder() {
984+
if (this.kotlinSerializationCborDecoder == null) {
985+
this.kotlinSerializationCborDecoder = (this.jacksonCborDecoder != null || JACKSON_CBOR_PRESENT ?
986+
new KotlinSerializationCborDecoder() : new KotlinSerializationCborDecoder(type -> true));
987+
}
988+
return this.kotlinSerializationCborDecoder;
989+
}
990+
991+
protected Encoder<?> getKotlinSerializationCborEncoder() {
992+
if (this.kotlinSerializationCborEncoder == null) {
993+
this.kotlinSerializationCborEncoder = (this.jacksonCborDecoder != null || JACKSON_CBOR_PRESENT ?
994+
new KotlinSerializationCborEncoder() : new KotlinSerializationCborEncoder(type -> true));
995+
}
996+
return this.kotlinSerializationCborEncoder;
997+
}
998+
999+
protected Decoder<?> getKotlinSerializationProtobufDecoder() {
1000+
if (this.kotlinSerializationProtobufDecoder == null) {
1001+
this.kotlinSerializationProtobufDecoder = new KotlinSerializationProtobufDecoder(type -> true);
1002+
}
1003+
return this.kotlinSerializationProtobufDecoder;
1004+
}
1005+
1006+
protected Encoder<?> getKotlinSerializationProtobufEncoder() {
1007+
if (this.kotlinSerializationProtobufEncoder == null) {
1008+
this.kotlinSerializationProtobufEncoder = new KotlinSerializationProtobufEncoder(type -> true);
1009+
}
1010+
return this.kotlinSerializationProtobufEncoder;
1011+
}
1012+
9221013

9231014
/**
9241015
* Default implementation of {@link CodecConfigurer.MultipartCodecs}.

0 commit comments

Comments
 (0)