Skip to content

Commit 5f54304

Browse files
committed
GH-3930: Add Jackson 3 support; deprecate Jackson 2
Fixes: #3930 * Manage dependencies, similar way we do for Jackson 2 * Add Jackson 3 counterparts for existing Jackson 2 based classes (mostly copy/paste) * Deprectate Jackson 2 classes * Update tests * Initial round of updates in docs Signed-off-by: Soby Chacko <[email protected]>
1 parent 8126e4b commit 5f54304

File tree

57 files changed

+3258
-189
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3258
-189
lines changed

build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ ext {
5656
hamcrestVersion = '3.0'
5757
hibernateValidationVersion = '8.0.2.Final'
5858
jacksonBomVersion = '2.19.1'
59+
jackson3Version = '3.0.0-rc5'
5960
jaywayJsonPathVersion = '2.9.0'
6061
junit4Version = '4.13.2'
6162
junitJupiterVersion = '5.13.3'
@@ -110,6 +111,7 @@ allprojects {
110111

111112
imports {
112113
mavenBom "com.fasterxml.jackson:jackson-bom:$jacksonBomVersion"
114+
mavenBom "tools.jackson:jackson-bom:$jackson3Version"
113115
mavenBom "org.junit:junit-bom:$junitJupiterVersion"
114116
mavenBom "io.micrometer:micrometer-bom:$micrometerVersion"
115117
mavenBom "io.micrometer:micrometer-tracing-bom:$micrometerTracingVersion"
@@ -263,6 +265,13 @@ project ('spring-kafka') {
263265
exclude group: 'org.jetbrains.kotlin'
264266
}
265267

268+
optionalApi 'tools.jackson.core:jackson-databind'
269+
optionalApi 'tools.jackson.datatype:jackson-datatype-joda'
270+
optionalApi 'tools.jackson.dataformat:jackson-dataformat-xml'
271+
optionalApi('tools.jackson.module:jackson-module-kotlin') {
272+
exclude group: 'org.jetbrains.kotlin'
273+
}
274+
266275
// Spring Data projection message binding support
267276
optionalApi ('org.springframework.data:spring-data-commons') {
268277
exclude group: 'org.springframework'

samples/sample-01/README.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This sample demonstrates a simple producer and consumer; the producer sends objects of type `Foo1` and the consumer receives objects of type `Foo2` (the objects have the same field, `foo`).
44

5-
The producer uses a `JsonSerializer`; the consumer uses the `ByteArrayDeserializer`, together with a `JsonMessageConverter` which converts to the type of the listener method argument.
5+
The producer uses a `JacksonJsonSerializer`; the consumer uses the `ByteArrayDeserializer`, together with a `JacksonJsonMessageConverter` which converts to the type of the listener method argument.
66

77
Run the application and use curl to send some data:
88

@@ -31,4 +31,4 @@ Console:
3131
...
3232
2018-11-05 10:12:33.537 INFO 41635 --- [ fooGroup-0-C-1] com.example.Application : Received: Foo2 [foo=fail]
3333
2018-11-05 10:12:43.359 INFO 41635 --- [ dltGroup-0-C-1] com.example.Application : Received from DLT: {"foo":"fail"}
34-
----
34+
----

samples/sample-02/README.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This sample demonstrates a simple producer and a multi-method consumer; the producer sends objects of types `Foo1` and `Bar1` and the consumer receives objects of type `Foo2` and `Bar2` (the objects have the same field, `foo`).
44

5-
The producer uses a `JsonSerializer`; the consumer uses a `ByteArrayDeserializer`, together with a `ByteArrayJsonMessageConverter` which converts to the required type of the listener method argument.
5+
The producer uses a `JacksonJsonSerializer`; the consumer uses a `ByteArrayJacksonDeserializer`, together with a `ByteArrayJacksonJsonMessageConverter` which converts to the required type of the listener method argument.
66
We can't infer the type in this case (because the type is used to choose the method to call).
77
We therefore configure type mapping on the producer and consumer side.
88
See the `application.yml` for the producer side and the `converter` bean on the consumer side.

spring-kafka-docs/src/main/antora/modules/ROOT/pages/kafka/receiving-messages/class-level-kafkalistener.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static class MultiListenerBean {
3535
Starting with version 2.1.3, you can designate a `@KafkaHandler` method as the default method that is invoked if there is no match on other methods.
3636
At most, one method can be so designated.
3737
When using `@KafkaHandler` methods, the payload must have already been converted to the domain object (so the match can be performed).
38-
Use a custom deserializer, the `JsonDeserializer`, or the `JsonMessageConverter` with its `TypePrecedence` set to `TYPE_ID`.
38+
Use a custom deserializer, the `JacksonJsonDeserializer`, or the `JacksonJsonMessageConverter` with its `TypePrecedence` set to `TYPE_ID`.
3939
See xref:kafka/serdes.adoc[Serialization, Deserialization, and Message Conversion] for more information.
4040

4141
IMPORTANT: Due to some limitations in the way Spring resolves method arguments, a default `@KafkaHandler` cannot receive discrete headers; it must use the `ConsumerRecordMetadata` as discussed in xref:kafka/receiving-messages/listener-annotation.adoc#consumer-record-metadata[Consumer Record Metadata].

spring-kafka-docs/src/main/antora/modules/ROOT/pages/kafka/serdes.adoc

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,18 @@ A `ToFromStringSerde` is also provided, for use with Kafka Streams.
7575
[[json-serde]]
7676
== JSON
7777

78-
Spring for Apache Kafka also provides `JsonSerializer` and `JsonDeserializer` implementations that are based on the
78+
Spring for Apache Kafka also provides `JacksonJsonSerializer` and `JacksonJsonDeserializer` implementations that are based on the
7979
Jackson JSON object mapper.
80-
The `JsonSerializer` allows writing any Java object as a JSON `byte[]`.
81-
The `JsonDeserializer` requires an additional `Class<?> targetType` argument to allow the deserialization of a consumed `byte[]` to the proper target object.
82-
The following example shows how to create a `JsonDeserializer`:
80+
The `JacksonJsonSerializer` allows writing any Java object as a JSON `byte[]`.
81+
The `JacksonJsonDeserializer` requires an additional `Class<?> targetType` argument to allow the deserialization of a consumed `byte[]` to the proper target object.
82+
The following example shows how to create a `JacksonJsonDeserializer`:
8383

8484
[source, java]
8585
----
86-
JsonDeserializer<Thing> thingDeserializer = new JsonDeserializer<>(Thing.class);
86+
JacksonJsonDeserializer<Thing> thingDeserializer = new JacksonJsonDeserializer<>(Thing.class);
8787
----
8888

89-
You can customize both `JsonSerializer` and `JsonDeserializer` with an `ObjectMapper`.
89+
You can customize both `JacksonJsonSerializer` and `JacksonJsonDeserializer` with an `ObjectMapper`.
9090
You can also extend them to implement some particular configuration logic in the `configure(Map<String, ?> configs, boolean isKey)` method.
9191

9292
Starting with version 2.3, all the JSON-aware components are configured by default with a `JacksonUtils.enhancedObjectMapper()` instance, which comes with the `MapperFeature.DEFAULT_VIEW_INCLUSION` and `DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES` features disabled.
@@ -104,22 +104,22 @@ They have no effect if you have provided `Serializer` and `Deserializer` instanc
104104
[[serdes-json-config]]
105105
=== Configuration Properties
106106

107-
* `JsonSerializer.ADD_TYPE_INFO_HEADERS` (default `true`): You can set it to `false` to disable this feature on the `JsonSerializer` (sets the `addTypeInfo` property).
108-
* `JsonSerializer.TYPE_MAPPINGS` (default `empty`): See xref:kafka/serdes.adoc#serdes-mapping-types[Mapping Types].
109-
* `JsonDeserializer.USE_TYPE_INFO_HEADERS` (default `true`): You can set it to `false` to ignore headers set by the serializer.
110-
* `JsonDeserializer.REMOVE_TYPE_INFO_HEADERS` (default `true`): You can set it to `false` to retain headers set by the serializer.
111-
* `JsonDeserializer.KEY_DEFAULT_TYPE`: Fallback type for deserialization of keys if no header information is present.
112-
* `JsonDeserializer.VALUE_DEFAULT_TYPE`: Fallback type for deserialization of values if no header information is present.
113-
* `JsonDeserializer.TRUSTED_PACKAGES` (default `java.util`, `java.lang`): Comma-delimited list of package patterns allowed for deserialization.
107+
* `JacksonJsonSerializer.ADD_TYPE_INFO_HEADERS` (default `true`): You can set it to `false` to disable this feature on the `JacksonJsonSerializer` (sets the `addTypeInfo` property).
108+
* `JacksonJsonSerializer.TYPE_MAPPINGS` (default `empty`): See xref:kafka/serdes.adoc#serdes-mapping-types[Mapping Types].
109+
* `JacksonJsonDeserializer.USE_TYPE_INFO_HEADERS` (default `true`): You can set it to `false` to ignore headers set by the serializer.
110+
* `JacksonJsonDeserializer.REMOVE_TYPE_INFO_HEADERS` (default `true`): You can set it to `false` to retain headers set by the serializer.
111+
* `JacksonJsonDeserializer.KEY_DEFAULT_TYPE`: Fallback type for deserialization of keys if no header information is present.
112+
* `JacksonJsonDeserializer.VALUE_DEFAULT_TYPE`: Fallback type for deserialization of values if no header information is present.
113+
* `JacksonJsonDeserializer.TRUSTED_PACKAGES` (default `java.util`, `java.lang`): Comma-delimited list of package patterns allowed for deserialization.
114114
`*` means deserializing all.
115-
* `JsonDeserializer.TYPE_MAPPINGS` (default `empty`): See xref:kafka/serdes.adoc#serdes-mapping-types[Mapping Types].
116-
* `JsonDeserializer.KEY_TYPE_METHOD` (default `empty`): See xref:kafka/serdes.adoc#serdes-type-methods[Using Methods to Determine Types].
117-
* `JsonDeserializer.VALUE_TYPE_METHOD` (default `empty`): See xref:kafka/serdes.adoc#serdes-type-methods[Using Methods to Determine Types].
115+
* `JacksonJsonDeserializer.TYPE_MAPPINGS` (default `empty`): See xref:kafka/serdes.adoc#serdes-mapping-types[Mapping Types].
116+
* `JacksonJsonDeserializer.KEY_TYPE_METHOD` (default `empty`): See xref:kafka/serdes.adoc#serdes-type-methods[Using Methods to Determine Types].
117+
* `JacksonJsonDeserializer.VALUE_TYPE_METHOD` (default `empty`): See xref:kafka/serdes.adoc#serdes-type-methods[Using Methods to Determine Types].
118118

119119
Starting with version 2.2, the type information headers (if added by the serializer) are removed by the deserializer.
120120
You can revert to the previous behavior by setting the `removeTypeHeaders` property to `false`, either directly on the deserializer or with the configuration property described earlier.
121121

122-
See also xref:tips.adoc#tip-json[Customizing the JsonSerializer and JsonDeserializer].
122+
See also xref:tips.adoc#tip-json[Customizing the JacksonJsonSerializer and JacksonJsonDeserializer].
123123

124124
IMPORTANT: Starting with version 2.8, if you construct the serializer or deserializer programmatically as shown in xref:kafka/serdes.adoc#prog-json[Programmatic Construction], the above properties will be applied by the factories, as long as you have not set any properties explicitly (using `set*()` methods or using the fluent API).
125125
Previously, when creating programmatically, the configuration properties were never applied; this is still the case if you explicitly set properties on the object directly.
@@ -409,7 +409,7 @@ Refer to the https://github.com/spring-projects/spring-retry[spring-retry] proje
409409
== Spring Messaging Message Conversion
410410

411411
Although the `Serializer` and `Deserializer` API is quite simple and flexible from the low-level Kafka `Consumer` and `Producer` perspective, you might need more flexibility at the Spring Messaging level, when using either `@KafkaListener` or {spring-integration-url}/kafka.html[Spring Integration's Apache Kafka Support].
412-
To let you easily convert to and from `org.springframework.messaging.Message`, Spring for Apache Kafka provides a `MessageConverter` abstraction with the `MessagingMessageConverter` implementation and its `JsonMessageConverter` (and subclasses) customization.
412+
To let you easily convert to and from `org.springframework.messaging.Message`, Spring for Apache Kafka provides a `MessageConverter` abstraction with the `MessagingMessageConverter` implementation and its `JacksonJsonMessageConverter` (and subclasses) customization.
413413
You can inject the `MessageConverter` into a `KafkaTemplate` instance directly and by using `AbstractKafkaListenerContainerFactory` bean definition for the `@KafkaListener.containerFactory()` property.
414414
The following example shows how to do so:
415415

@@ -443,15 +443,15 @@ With a class-level `@KafkaListener`, the payload type is used to select which `@
443443

444444
[NOTE]
445445
====
446-
On the consumer side, you can configure a `JsonMessageConverter`; it can handle `ConsumerRecord` values of type `byte[]`, `Bytes` and `String` so should be used in conjunction with a `ByteArrayDeserializer`, `BytesDeserializer` or `StringDeserializer`.
446+
On the consumer side, you can configure a `JacksonJsonMessageConverter`; it can handle `ConsumerRecord` values of type `byte[]`, `Bytes` and `String` so should be used in conjunction with a `ByteArrayDeserializer`, `BytesDeserializer` or `StringDeserializer`.
447447
(`byte[]` and `Bytes` are more efficient because they avoid an unnecessary `byte[]` to `String` conversion).
448-
You can also configure the specific subclass of `JsonMessageConverter` corresponding to the deserializer, if you so wish.
448+
You can also configure the specific subclass of `JacksonJsonMessageConverter` corresponding to the deserializer, if you so wish.
449449
450450
On the producer side, when you use Spring Integration or the `KafkaTemplate.send(Message<?> message)` method (see xref:kafka/sending-messages.adoc#kafka-template[Using `KafkaTemplate`]), you must configure a message converter that is compatible with the configured Kafka `Serializer`.
451451
452-
* `StringJsonMessageConverter` with `StringSerializer`
453-
* `BytesJsonMessageConverter` with `BytesSerializer`
454-
* `ByteArrayJsonMessageConverter` with `ByteArraySerializer`
452+
* `StringJacksonJsonMessageConverter` with `StringSerializer`
453+
* `BytesJacksonJsonMessageConverter` with `BytesSerializer`
454+
* `ByteArrayJacksonJsonMessageConverter` with `ByteArraySerializer`
455455
456456
Again, using `byte[]` or `Bytes` is more efficient because they avoid a `String` to `byte[]` conversion.
457457
@@ -513,7 +513,7 @@ public void projection(SomeSample in) {
513513
Accessor methods will be used to lookup the property name as field in the received JSON document by default.
514514
The `@JsonPath` expression allows customization of the value lookup, and even to define multiple JSON Path expressions, to look up values from multiple places until an expression returns an actual value.
515515

516-
To enable this feature, use a `ProjectingMessageConverter` configured with an appropriate delegate converter (used for outbound conversion and converting non-projection interfaces).
516+
To enable this feature, use a `JacksonProjectingMessageConverter` configured with an appropriate delegate converter (used for outbound conversion and converting non-projection interfaces).
517517
You must also add `spring-data:spring-data-commons` and `com.jayway.jsonpath:json-path` to the classpath.
518518

519519
When used as the parameter to a `@KafkaListener` method, the interface type is automatically passed to the converter as normal.
@@ -673,11 +673,11 @@ When using Spring Boot, this property name is `spring.kafka.consumer.properties.
673673
[[payload-conversion-with-batch]]
674674
== Payload Conversion with Batch Listeners
675675

676-
You can also use a `JsonMessageConverter` within a `BatchMessagingMessageConverter` to convert batch messages when you use a batch listener container factory.
676+
You can also use a `JacksonJsonMessageConverter` within a `BatchMessagingMessageConverter` to convert batch messages when you use a batch listener container factory.
677677
See xref:kafka/serdes.adoc[Serialization, Deserialization, and Message Conversion] and xref:kafka/serdes.adoc#messaging-message-conversion[Spring Messaging Message Conversion] for more information.
678678

679679
By default, the type for the conversion is inferred from the listener argument.
680-
If you configure the `JsonMessageConverter` with a `DefaultJackson2TypeMapper` that has its `TypePrecedence` set to `TYPE_ID` (instead of the default `INFERRED`), the converter uses the type information in headers (if present) instead.
680+
If you configure the `JacksonJsonMessageConverter` with a `DefaultJackson2TypeMapper` that has its `TypePrecedence` set to `TYPE_ID` (instead of the default `INFERRED`), the converter uses the type information in headers (if present) instead.
681681
This allows, for example, listener methods to be declared with interfaces instead of concrete classes.
682682
Also, the type converter supports mapping, so the deserialization can be to a different type than the source (as long as the data is compatible).
683683
This is also useful when you use xref:kafka/receiving-messages/class-level-kafkalistener.adoc[class-level `@KafkaListener` instances] where the payload must have already been converted to determine which method to invoke.

spring-kafka-docs/src/main/antora/modules/ROOT/pages/streams.adoc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,24 +151,24 @@ streamsBuilderFactoryBean.addListener(new KafkaStreamsMicrometerListener(meterRe
151151
[[serde]]
152152
== Streams JSON Serialization and Deserialization
153153

154-
For serializing and deserializing data when reading or writing to topics or state stores in JSON format, Spring for Apache Kafka provides a `JsonSerde` implementation that uses JSON, delegating to the `JsonSerializer` and `JsonDeserializer` described in xref:kafka/serdes.adoc[Serialization, Deserialization, and Message Conversion].
155-
The `JsonSerde` implementation provides the same configuration options through its constructor (target type or `ObjectMapper`).
156-
In the following example, we use the `JsonSerde` to serialize and deserialize the `Cat` payload of a Kafka stream (the `JsonSerde` can be used in a similar fashion wherever an instance is required):
154+
For serializing and deserializing data when reading or writing to topics or state stores in JSON format, Spring for Apache Kafka provides a `JacksonJsonSerde` implementation that uses JSON, delegating to the `JacksonJsonSerializer` and `JacksonJsonDeserializer` described in xref:kafka/serdes.adoc[Serialization, Deserialization, and Message Conversion].
155+
The `JacksonJsonSerde` implementation provides the same configuration options through its constructor (target type or `ObjectMapper`).
156+
In the following example, we use the `JacksonJsonSerde` to serialize and deserialize the `Cat` payload of a Kafka stream (the `JacksonJsonSerde` can be used in a similar fashion wherever an instance is required):
157157

158158
[source,java]
159159
----
160-
stream.through(Serdes.Integer(), new JsonSerde<>(Cat.class), "cats");
160+
stream.through(Serdes.Integer(), new JacksonJsonSerde<>(Cat.class), "cats");
161161
----
162162

163163
When constructing the serializer/deserializer programmatically for use in the producer/consumer factory, since version 2.3, you can use the fluent API, which simplifies configuration.
164164

165165
[source, java]
166166
----
167167
stream.through(
168-
new JsonSerde<>(MyKeyType.class)
168+
new JackonJsonSerde<>(MyKeyType.class)
169169
.forKeys()
170170
.noTypeInfo(),
171-
new JsonSerde<>(MyValueType.class)
171+
new JacksonJsonSerde<>(MyValueType.class)
172172
.noTypeInfo(),
173173
"myTypes");
174174
----

spring-kafka-docs/src/main/java/org/springframework/kafka/jdocs/requestreply/Application.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
3939
import org.springframework.kafka.requestreply.ReplyingKafkaTemplate;
4040
import org.springframework.kafka.requestreply.RequestReplyTypedMessageFuture;
41-
import org.springframework.kafka.support.converter.ByteArrayJsonMessageConverter;
41+
import org.springframework.kafka.support.converter.ByteArrayJacksonJsonMessageConverter;
4242
import org.springframework.messaging.handler.annotation.SendTo;
4343
import org.springframework.messaging.support.MessageBuilder;
4444

@@ -89,7 +89,7 @@ ReplyingKafkaTemplate<String, String, String> template(
8989
replyContainer.getContainerProperties().setGroupId("request.replies");
9090
ReplyingKafkaTemplate<String, String, String> template =
9191
new ReplyingKafkaTemplate<>(pf, replyContainer);
92-
template.setMessageConverter(new ByteArrayJsonMessageConverter());
92+
template.setMessageConverter(new ByteArrayJacksonJsonMessageConverter());
9393
template.setDefaultTopic("requests");
9494
return template;
9595
}

spring-kafka-docs/src/main/kotlin/org/springframework/kafka/kdocs/requestreply/Application.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import org.springframework.kafka.core.KafkaTemplate
3131
import org.springframework.kafka.core.ProducerFactory
3232
import org.springframework.kafka.requestreply.ReplyingKafkaTemplate
3333
import org.springframework.kafka.requestreply.RequestReplyTypedMessageFuture
34-
import org.springframework.kafka.support.converter.ByteArrayJsonMessageConverter
34+
import org.springframework.kafka.support.converter.ByteArrayJacksonJsonMessageConverter
3535
import org.springframework.messaging.handler.annotation.SendTo
3636
import org.springframework.messaging.support.MessageBuilder
3737
import java.util.concurrent.TimeUnit
@@ -76,7 +76,7 @@ class Application {
7676
val replyContainer = factory.createContainer("replies")
7777
replyContainer.containerProperties.setGroupId("request.replies")
7878
val template = ReplyingKafkaTemplate<String, String, String>(pf, replyContainer)
79-
template.messageConverter = ByteArrayJsonMessageConverter()
79+
template.messageConverter = ByteArrayJacksonJsonMessageConverter()
8080
template.setDefaultTopic("requests")
8181
return template
8282
}

0 commit comments

Comments
 (0)