Add Jackson 3 support#945
Conversation
| ) | ||
|
|
||
| @Input | ||
| val jacksonVersions: SetProperty<JacksonVersion> = |
There was a problem hiding this comment.
wdyt of providing a customer-facing property that Gradle plugin consumers can override to specify the Jackson version to use if we detect it incorrectly (so they are not blocked)?
The con is that it's yet another prop that we need to maintain
| val jacksonVersions: SetProperty<JacksonVersion> = | ||
| objectFactory.setProperty(JacksonVersion::class.java).convention( | ||
| project.configurations | ||
| .named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) |
There was a problem hiding this comment.
Shall we consider runtime dependencies as well?
Thinking of a (weird) setup where Jackson is only present on the runtime classpath via transitive dependencies
There was a problem hiding this comment.
Since the annotations are emitted into main source code, I think if we add annotations that aren't on the compile classpath but are on the runtime classpath for some reason, they wouldn't compile. I agree with adding a customer-facing property from your other comment to give the user a mechanism to override if the detection is incorrect and is more robust/correct than using runtime detection
…generateKotlinNullableClasses
Closes #899
Changes
This PR re-introduces support for Jackson 3 (now configured by default in Spring Boot 4 projects) for projects that use the Kotlin 2 generator (
generateKotlinNullableClasses = true) since these are the ones that use the builder, by detecting which versions of Jackson annotations are available on the compile classpath and adding annotations for the available version(s). If neither version is found, codegen defaults to Jackson 2 annotations for backwards compatibility. If both versions are found, codegen adds both annotations and defers to runtime selection behavior.Affected annotations are:
@JsonDeserializeincom.fasterxml.jackson.databind.annotation(Jackson 2) andtools.jackson.databind.annotation(Jackson 3)@JsonPOJOBuilderincom.fasterxml.jackson.databind.annotation(Jackson 2) andtools.jackson.databind.annotation(Jackson 3)Examples
Example with both Jackson versions:
Example with single Jackson version:
How Jackson version detection works
The task has two inputs that decide which Jackson annotations get generated (
@JsonDeserialize/@JsonPOJOBuilder):jacksonVersionOverride— optional user override:["2"](com.fasterxml.jackson),["3"](tools.jackson), or["2", "3"]. Anything else fails the build. Empty = auto-detect.compileClasspathfor thejackson-databindmodule and keys on group (com.fasterxml.jackson.core→ 2,tools.jackson.core→ 3; the group is the only discriminator since both share the artifact name).The override wins when set; otherwise detection is used. This only runs when
generateKotlinNullableClassesis enabled (the only path that emits Jackson annotations), and an empty result defaults to Jackson 2.Detection uses a lazy
rootComponentprovider so it's configuration-cache safe, which raises the minimum Gradle version to 7.4.Safe with the Jakarta EE migration plugin
Detection reads only the dependency graph metadata — it walks
resolutionResult.rootComponentand never callsgetFiles()/resolvedArtifacts. The Jakarta plugin is an artifact transform that only fires when artifact files are requested, so a metadata-only read never triggers it (and never forces sibling modules to build). That eager artifact-resolution path is what broke the earlierresolvedArtifacts-based attempt.Safe with the Gradle configuration cache
The task never holds a
ConfigurationorProject(neither is cc-serializable). Detection is a lazyrootComponentprovider (replacing the earlier eagerallComponentsgetter) that's resolved only at task execution — after all dependencies are declared — and the override is a plainListProperty<String>, so everything serialized is cc-safe. A functional test runsgenerateJavatwice with--configuration-cache --configuration-cache-problems=failand asserts store-then-reuse