Migrating Spring Boot 4 Workshop Examples to Jackson 3

When moving examples to Spring Boot 4, the visible changes are usually Spring Framework 7, starter names, and test package moves. The JSON line is easier to miss, but it is often where examples start to fail.
Spring Boot 4 uses Jackson 3 as its preferred default JSON library. Jackson 2 support remains a temporary
migration path, not the long-term line for new Spring Boot 4 examples. If a Spring Boot 4 workshop module
still depends on bluetape4k-jackson2 or imports Jackson 2 mapper packages, the example carries two JSON
baselines at once.
Examples are not throwaway samples. Users copy them when starting real projects. If an example carries the wrong compatibility line, the first failure can appear before the interesting code even runs.
The Problem Is the Compatibility Line
Section titled “The Problem Is the Compatibility Line”In bluetape4k-workshop, the Spring Kafka request-reply example and Spring Data Elasticsearch example still
referenced bluetape4k-jackson2. The migration aligned three boundaries rather than merely increasing a
version number.
The rule is not “delete every trace of Jackson 2.” The rule is to make the example line explicit.
| Check | Spring Boot 4 example line |
|---|---|
| bluetape4k JSON helper | bluetape4k-jackson3 |
| Jackson package | tools.jackson.* |
| Jackson BOM | tools.jackson:jackson-bom |
| Spring Boot line | Spring Boot 4 BOM and starters |
| Exception | Upstream modules that still expose Jackson 2 APIs, or annotation packages |
The workshop root build imports both Spring Boot 4 and Jackson 3 BOMs.
dependencyManagement { imports { mavenBom(rootLibs.spring.boot4.dependencies.get().toString()) mavenBom(rootLibs.jackson3.bom.get().toString()) }}The version catalog still contains both Jackson lines.
# Jackson 2jackson-bom = { module = "com.fasterxml.jackson:jackson-bom", version.ref = "jackson" }jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind" }
# Jackson 3jackson3-bom = { module = "tools.jackson:jackson-bom", version.ref = "jackson3" }jackson3-databind = { module = "tools.jackson.core:jackson-databind" }jackson3-module-kotlin = { module = "tools.jackson.module:jackson-module-kotlin" }Having both in the catalog does not mean every module can choose freely. The catalog is the parts shelf. The example baseline decides which line is assembled.

Align the Dependency Boundary
Section titled “Align the Dependency Boundary”messaging/kafka-reply demonstrates Spring Kafka request-reply flow. On the Spring Boot 4 line, it declares
Jackson 3 directly.
dependencies { api(libs.bluetape4k.jackson3) api(libs.jackson3.databind) api(libs.jackson3.module.kotlin) api(libs.jackson3.module.blackbird)
implementation(libs.bluetape4k.kafka4) implementation(libs.bluetape4k.coroutines) implementation(libs.spring.boot.starter.webflux.lib)}This is not a feature add-on. It aligns the example with the world it claims to show: Spring Boot 4, Spring
Kafka 4, Jackson 3, and bluetape4k-jackson3.
spring-data/elasticsearch follows the same line.
dependencies { implementation(libs.spring.boot.starter.data.elasticsearch.lib) implementation(libs.spring.boot.starter.webflux.lib)
implementation(libs.bluetape4k.jackson3) implementation(libs.bluetape4k.coroutines)}After aligning dependencies, inspect imports and mapper beans.
Align the Code API Boundary
Section titled “Align the Code API Boundary”Jackson 3 changed the dependency group and package names. The Spring Boot migration guide calls out the move
from com.fasterxml.jackson to tools.jackson. Workshop code should look like this when it configures a
Jackson 3 mapper.
import io.bluetape4k.jackson3.Jacksonimport tools.jackson.databind.json.JsonMapper
@Configuration(proxyBeanMethods = false)class JacksonConfig {
@Bean fun jsonMapper(): JsonMapper = Jackson.defaultJsonMapper
}Jackson.defaultJsonMapper from bluetape4k-jackson3 provides a Kotlin-ready default mapper with the common
modules already wired. Examples do not have to rebuild the same setup in every module.
When extra settings are needed, use rebuild().
protected val defaultMapper: JsonMapper by lazy { Jackson.defaultJsonMapper .rebuild() .apply { configure(SerializationFeature.INDENT_OUTPUT, true) } .build()}That is a useful workshop pattern. The reader sees where to add local configuration without learning how to reassemble every Jackson module first.
com.fasterxml.jackson.annotation Is Different
Section titled “com.fasterxml.jackson.annotation Is Different”A naive search-and-delete pass over com.fasterxml.jackson is dangerous.
Jackson 3 moved most mapper packages to tools.jackson, but annotations remain under
com.fasterxml.jackson.annotation for compatibility. These imports can still be valid:
import com.fasterxml.jackson.annotation.JsonViewUse a classification rule instead.
| Search result | Action |
|---|---|
com.fasterxml.jackson.databind.* | Usually move to tools.jackson.databind.* in Jackson 3 modules |
com.fasterxml.jackson.module.* | Usually move to tools.jackson.module.* in Jackson 3 modules |
com.fasterxml.jackson.annotation.* | Do not delete blindly; verify whether the annotation is still valid |
bluetape4k-jackson2 | In Spring Boot 4 examples, evaluate a move to bluetape4k-jackson3 |
Migration needs classification, not string replacement.
Align Configuration and Verification
Section titled “Align Configuration and Verification”Dependencies and imports are not the whole migration. Spring Boot 4 with Jackson 3 can also change how
spring.jackson.* properties behave.
During the Exposed example rewrite, this familiar setting broke application-context loading:
spring: jackson: serialization: write-dates-as-timestamps: falseIn that example, enum conversion did not map write-dates-as-timestamps to Jackson 3’s
WRITE_DATES_AS_TIMESTAMPS. The fix was to remove the spring.jackson section and configure the mapper in
code. This does not mean every spring.jackson.* property is invalid under Jackson 3. Each property must be
verified against the Spring Boot 4 and Jackson 3 combination in use.
spring.jackson.use-jackson2-defaults=true does not enable Jackson 2. It adjusts Jackson 3 mapper defaults to
behave more like Spring Boot 3 with Jackson 2. Applications that must keep Jackson 2 need to evaluate the
separate spring-boot-jackson2 compatibility module and the spring.jackson2.* configuration namespace.
Compatibility paths should carry a reason and an exit condition instead of silently becoming the example’s
default.
Some Boundaries Cannot Move Yet
Section titled “Some Boundaries Cannot Move Yet”Forcing every module to Jackson 3 may look clean, but upstream APIs decide the real boundary.
The Quarkus Jackson extension modules remained unchanged because their local source still exposed Jackson 2 APIs. The Spring Data Elasticsearch modules also contain disabled tests that document a Jackson 2 client boundary colliding with the Spring Boot 4 Jackson 3 line.
@Disabled("Elasticsearch Client 가 Jackson2 를 사용합니다. Spring Boot 4 는 Jackson 3를 사용해서 충돌이 발생합니다.")That note identifies a remaining compatibility debt; it is not proof that the migration is complete. Users need to know which boundary moved and which one still waits for upstream support.
Migration Checklist
Section titled “Migration Checklist”Use this checklist when creating or cleaning a Spring Boot 4 workshop module.
| Step | Check | Example |
|---|---|---|
| 1 | Module baseline | Confirm it is a Spring Boot 4 module |
| 2 | bluetape4k JSON helper | Prefer bluetape4k-jackson3 over bluetape4k-jackson2 |
| 3 | Jackson dependency | Use jackson3-databind, jackson3-module-kotlin |
| 4 | Imports | Use tools.jackson.databind.*, tools.jackson.module.* |
| 5 | Annotation exception | Do not blindly delete com.fasterxml.jackson.annotation.* |
| 6 | Mapper bean | Prefer JsonMapper and Jackson.defaultJsonMapper |
| 7 | Config files | Re-check spring.jackson.* properties under Jackson 3 |
| 8 | Upstream exceptions | Document clients or extensions that still require Jackson 2 |
| 9 | Verification | Run the relevant module testClasses or test task |
The related module compilation and test configuration were verified together:
./gradlew :messaging-kafka-reply:testClasses :spring-data-elasticsearch:testClassesOne practical lesson came from a build-configuration failure: a duplicate version-catalog alias blocked Gradle
configuration before module validation could even start. When a dependency migration fails before task
selection, inspect libs.versions.toml before blaming the module tests.
Examples Document the Line
Section titled “Examples Document the Line”The point of moving Spring Boot 4 workshop modules to Jackson 3 was not novelty. The point was to give users a clean starting line.
A Spring Boot 4 example that still carries Jackson 2 helpers teaches two things at once: the intended example and a classpath mixture the user should probably avoid. The second lesson is not worth exporting.
Spring Boot 4, Jackson 3, bluetape4k-jackson3, tools.jackson.*, and JsonMapper should point in the same
direction. When they do, the example becomes quieter for the right reason: the compatibility line is aligned.
Resources
Section titled “Resources”- Spring Boot JSON documentation
- Spring Boot 4.0 Migration Guide — Upgrading Jackson
- Jackson 3 Migration Guide
build.gradle.ktsgradle/libs.versions.tomlmessaging/kafka-reply/build.gradle.ktsspring-data/elasticsearch/build.gradle.ktsjson/jsonview-examples/JacksonConfig.ktjson/jackson-examples/AbstractJacksonTest.kt
Comments
Leave a note or reaction with your GitHub account.