Skip to content

Commit 428a027

Browse files
committed
Reinstate Jackson2 config as deprecated SpringDataJacksonConfiguration, introduce SpringDataJackson3Configuration.
1 parent 0e6f191 commit 428a027

14 files changed

+319
-82
lines changed

src/main/antora/modules/ROOT/pages/repositories/core-extensions-web.adoc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,9 @@ By default, the assembler points to the controller method it was invoked in, but
290290
== Spring Data Jackson Modules
291291

292292
The core module, and some of the store specific ones, ship with a set of Jackson Modules for types, like `org.springframework.data.geo.Distance` and `org.springframework.data.geo.Point`, used by the Spring Data domain. +
293-
Those Modules are imported once xref:repositories/core-extensions.adoc#core.web[web support] is enabled and `com.fasterxml.jackson.databind.ObjectMapper` is available.
293+
Those modules are imported once xref:repositories/core-extensions.adoc#core.web[web support] is enabled and `tools.jackson.databind.ObjectMapper` is available.
294294

295-
During initialization `SpringDataJacksonModules`, like the `SpringDataJacksonConfiguration`, get picked up by the infrastructure, so that the declared ``com.fasterxml.jackson.databind.Module``s are made available to the Jackson `ObjectMapper`.
295+
During initialization `SpringDataJackson3Modules`, like the `SpringDataJackson3Configuration`, get picked up by the infrastructure, so that the declared ``tools.jackson.databind.JacksonModule``s are made available to the Jackson `ObjectMapper`.
296296

297297
Data binding mixins for the following domain types are registered by the common infrastructure.
298298

@@ -306,10 +306,15 @@ org.springframework.data.geo.Polygon
306306

307307
[NOTE]
308308
====
309-
The individual module may provide additional `SpringDataJacksonModules`. +
309+
The individual module may provide additional `SpringDataJackson3Modules`. +
310310
Please refer to the store specific section for more details.
311311
====
312312

313+
[NOTE]
314+
====
315+
Jackson 2 support is deprecated and will be removed in a future release.
316+
====
317+
313318
[[core.web.binding]]
314319
== Web Databinding Support
315320

@@ -341,7 +346,7 @@ Nested projections are supported as described in xref:repositories/projections.a
341346
If the method returns a complex, non-interface type, a Jackson `ObjectMapper` is used to map the final value.
342347

343348
For Spring MVC, the necessary converters are registered automatically as soon as `@EnableSpringDataWebSupport` is active and the required dependencies are available on the classpath.
344-
For usage with `RestTemplate`, register a `ProjectingJackson2HttpMessageConverter` (JSON) or `XmlBeamHttpMessageConverter` manually.
349+
For usage with `RestTemplate`, register a `ProjectingJacksonHttpMessageConverter` (JSON) or `XmlBeamHttpMessageConverter` manually.
345350

346351
For more information, see the https://github.com/spring-projects/spring-data-examples/tree/main/web/projection[web projection example] in the canonical https://github.com/spring-projects/spring-data-examples[Spring Data Examples repository].
347352

src/main/java/org/springframework/data/geo/GeoModule.java

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

2929

3030
/**
31-
* Custom module to deserialize the geo-spatial value objects using Jackson 2.
31+
* Custom module to deserialize the geo-spatial value objects using Jackson 3.
3232
*
3333
* @author Oliver Gierke
3434
* @since 1.8

src/main/java/org/springframework/data/web/aot/WebRuntimeHints.java

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,32 @@
2323
import org.springframework.aot.hint.TypeReference;
2424
import org.springframework.data.web.PagedModel;
2525
import org.springframework.data.web.config.EnableSpringDataWebSupport;
26+
import org.springframework.data.web.config.SpringDataJackson3Configuration;
2627
import org.springframework.data.web.config.SpringDataJacksonConfiguration.PageModule;
2728
import org.springframework.util.ClassUtils;
2829

2930
/**
3031
* {@link RuntimeHintsRegistrar} providing hints for web usage.
3132
*
3233
* @author Christoph Strobl
34+
* @author Mark Paluch
3335
* @since 3.2.3
3436
*/
3537
class WebRuntimeHints implements RuntimeHintsRegistrar {
3638

39+
private static final boolean JACKSON2_PRESENT = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
40+
WebRuntimeHints.class.getClassLoader());
41+
42+
private static final boolean JACKSON3_PRESENT = ClassUtils.isPresent("tools.jackson.databind.ObjectMapper",
43+
WebRuntimeHints.class.getClassLoader());
44+
3745
@Override
3846
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
3947

4048
hints.reflection().registerType(org.springframework.data.web.config.SpringDataWebSettings.class, hint -> hint
4149
.withMembers(MemberCategory.INVOKE_DECLARED_METHODS).onReachableType(EnableSpringDataWebSupport.class));
4250

43-
if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
44-
|| ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", classLoader)) {
51+
if (JACKSON2_PRESENT || JACKSON3_PRESENT) {
4552

4653
// Page Model for Jackson Rendering
4754
hints.reflection().registerType(org.springframework.data.web.PagedModel.class,
@@ -50,24 +57,53 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader)
5057
hints.reflection().registerType(PagedModel.PageMetadata.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
5158
MemberCategory.INVOKE_PUBLIC_METHODS);
5259

53-
// Type that might not be seen otherwise
54-
hints.reflection().registerType(TypeReference.of("org.springframework.data.domain.Unpaged"),
55-
hint -> hint.onReachableType(PageModule.class));
56-
57-
// Jackson Converters used via @JsonSerialize in SpringDataJacksonConfiguration
58-
hints.reflection().registerType(
59-
TypeReference
60-
.of("org.springframework.data.web.config.SpringDataJacksonConfiguration$PageModule$PageModelConverter"),
61-
hint -> {
62-
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
63-
hint.onReachableType(PageModule.class);
64-
});
65-
hints.reflection().registerType(TypeReference.of(
66-
"org.springframework.data.web.config.SpringDataJacksonConfiguration$PageModule$PlainPageSerializationWarning"),
67-
hint -> {
68-
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
69-
hint.onReachableType(PageModule.class);
70-
});
60+
hints.reflection().registerType(TypeReference.of("org.springframework.data.domain.Unpaged"));
61+
62+
if (JACKSON2_PRESENT) {
63+
contributeJackson2Hints(hints);
64+
}
65+
66+
if (JACKSON3_PRESENT) {
67+
contributeJackson3Hints(hints);
68+
}
7169
}
7270
}
71+
72+
@SuppressWarnings("removal")
73+
private static void contributeJackson2Hints(RuntimeHints hints) {
74+
75+
// Jackson Converters used via @JsonSerialize in SpringDataJacksonConfiguration
76+
hints.reflection().registerType(
77+
TypeReference
78+
.of("org.springframework.data.web.config.SpringDataJacksonConfiguration$PageModule$PageModelConverter"),
79+
hint -> {
80+
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
81+
hint.onReachableType(PageModule.class);
82+
});
83+
hints.reflection().registerType(TypeReference.of(
84+
"org.springframework.data.web.config.SpringDataJacksonConfiguration$PageModule$PlainPageSerializationWarning"),
85+
hint -> {
86+
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
87+
hint.onReachableType(PageModule.class);
88+
});
89+
}
90+
91+
private static void contributeJackson3Hints(RuntimeHints hints) {
92+
93+
// Jackson Converters used via @JsonSerialize in SpringDataJacksonConfiguration
94+
hints.reflection().registerType(
95+
TypeReference
96+
.of("org.springframework.data.web.config.SpringDataJackson3Configuration$PageModule$PageModelConverter"),
97+
hint -> {
98+
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
99+
hint.onReachableType(SpringDataJackson3Configuration.PageModule.class);
100+
});
101+
hints.reflection().registerType(TypeReference.of(
102+
"org.springframework.data.web.config.SpringDataJackson3Configuration$PageModule$PlainPageSerializationWarning"),
103+
hint -> {
104+
hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
105+
hint.onReachableType(SpringDataJackson3Configuration.PageModule.class);
106+
});
107+
}
108+
73109
}

src/main/java/org/springframework/data/web/config/EnableSpringDataWebSupport.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,14 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
141141
.orElseGet(() -> SpringDataWebConfiguration.class.getName()));
142142

143143
resourceLoader//
144-
.filter(it -> ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", it)
145-
|| ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", it))//
144+
.filter(it -> ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", it))//
146145
.map(it -> SpringFactoriesLoader.loadFactoryNames(SpringDataJacksonModules.class, it))//
147-
.ifPresent(it -> imports.addAll(it));
146+
.ifPresent(imports::addAll);
147+
148+
resourceLoader//
149+
.filter(it -> ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", it))//
150+
.map(it -> SpringFactoriesLoader.loadFactoryNames(SpringDataJackson3Modules.class, it))//
151+
.ifPresent(imports::addAll);
148152

149153
return imports.toArray(new String[imports.size()]);
150154
}

src/main/java/org/springframework/data/web/config/SpringDataJackson2Configuration.java renamed to src/main/java/org/springframework/data/web/config/SpringDataJackson3Configuration.java

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
*/
1616
package org.springframework.data.web.config;
1717

18+
import tools.jackson.databind.annotation.JsonSerialize;
19+
import tools.jackson.databind.module.SimpleModule;
20+
import tools.jackson.databind.ser.BeanPropertyWriter;
21+
import tools.jackson.databind.ser.ValueSerializerModifier;
22+
import tools.jackson.databind.ser.std.ToStringSerializerBase;
23+
import tools.jackson.databind.util.StdConverter;
24+
1825
import java.io.Serial;
1926
import java.util.List;
2027

@@ -31,36 +38,25 @@
3138
import org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode;
3239
import org.springframework.util.ClassUtils;
3340

34-
import com.fasterxml.jackson.databind.BeanDescription;
35-
import com.fasterxml.jackson.databind.SerializationConfig;
36-
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
37-
import com.fasterxml.jackson.databind.module.SimpleModule;
38-
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
39-
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
40-
import com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase;
41-
import com.fasterxml.jackson.databind.util.StdConverter;
42-
4341
/**
44-
* JavaConfig class to export Jackson 2-specific configuration for easier migration to Jackson 3.
42+
* JavaConfig class to export Jackson 3-specific configuration.
4543
*
4644
* @author Oliver Gierke
4745
* @author Mark Paluch
4846
* @since 4.0
49-
* @deprecated since 4.0, in favor of {@link SpringDataJacksonConfiguration} which uses Jackson 3.
5047
*/
51-
@Deprecated(since = "4.0", forRemoval = true)
52-
public class SpringDataJackson2Configuration implements SpringDataJacksonModules {
48+
public class SpringDataJackson3Configuration implements SpringDataJackson3Modules {
5349

5450
@Nullable
5551
@Autowired(required = false) SpringDataWebSettings settings;
5652

5753
@Bean
58-
public GeoModule jacksonGeoModule() {
54+
public GeoModule jackson3GeoModule() {
5955
return new GeoModule();
6056
}
6157

6258
@Bean
63-
public PageModule pageModule() {
59+
public PageModule jackson3pageModule() {
6460
return new PageModule(settings);
6561
}
6662

@@ -102,15 +98,13 @@ public PageModule(@Nullable SpringDataWebSettings settings) {
10298
}
10399

104100
/**
105-
* A Jackson serializer rendering instances of {@link org.springframework.data.domain.Unpaged} as {@code INSTANCE}
101+
* A Jackson serializer rendering instances of {@code org.springframework.data.domain.Unpaged} as {@code INSTANCE}
106102
* as it was previous rendered.
107103
*
108104
* @author Oliver Drotbohm
109105
*/
110106
static class UnpagedAsInstanceSerializer extends ToStringSerializerBase {
111107

112-
private static final @Serial long serialVersionUID = -1213451755610144637L;
113-
114108
public UnpagedAsInstanceSerializer() {
115109
super(Object.class);
116110
}
@@ -122,7 +116,7 @@ public String valueToString(@Nullable Object value) {
122116
}
123117

124118
@JsonSerialize(converter = PageModelConverter.class)
125-
abstract class WrappingMixing {}
119+
abstract static class WrappingMixing {}
126120

127121
static class PageModelConverter extends StdConverter<Page<?>, PagedModel<?>> {
128122

@@ -133,11 +127,11 @@ static class PageModelConverter extends StdConverter<Page<?>, PagedModel<?>> {
133127
}
134128

135129
/**
136-
* A {@link BeanSerializerModifier} that logs a warning message if an instance of {@link Page} will be rendered.
130+
* A {@link ValueSerializerModifier} that logs a warning message if an instance of {@link Page} will be rendered.
137131
*
138132
* @author Oliver Drotbohm
139133
*/
140-
static class WarningLoggingModifier extends BeanSerializerModifier {
134+
static class WarningLoggingModifier extends ValueSerializerModifier {
141135

142136
private static final Logger LOGGER = LoggerFactory.getLogger(WarningLoggingModifier.class);
143137
private static final String MESSAGE = """
@@ -151,8 +145,8 @@ static class WarningLoggingModifier extends BeanSerializerModifier {
151145
private boolean warningRendered = false;
152146

153147
@Override
154-
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
155-
List<BeanPropertyWriter> beanProperties) {
148+
public List<BeanPropertyWriter> changeProperties(tools.jackson.databind.SerializationConfig config,
149+
tools.jackson.databind.BeanDescription.Supplier beanDesc, List<BeanPropertyWriter> beanProperties) {
156150

157151
if (Page.class.isAssignableFrom(beanDesc.getBeanClass()) && !warningRendered) {
158152

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2016-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.web.config;
17+
18+
/**
19+
* Marker interface to describe configuration classes that ship Jackson modules that are supposed to be added to the
20+
* Jackson 3 {@link tools.jackson.databind.ObjectMapper} configured for {@link EnableSpringDataWebSupport}.
21+
*
22+
* @author Oliver Gierke
23+
* @author Mark Paluch
24+
* @since 4.0
25+
*/
26+
public interface SpringDataJackson3Modules {}

0 commit comments

Comments
 (0)