Update Jackson support to require Jackson 3

Closes gh-45535
This commit is contained in:
Andy Wilkinson
2025-09-02 11:37:09 +01:00
parent 8bfb170ebc
commit d353038c58
176 changed files with 1420 additions and 1678 deletions
+1 -2
View File
@@ -37,8 +37,8 @@ dependencies {
optional(project(":module:spring-boot-health"))
optional(project(":module:spring-boot-micrometer-metrics"))
optional(project(":module:spring-boot-micrometer-observation"))
optional(project(":module:spring-boot-jackson"))
optional(project(":module:spring-boot-validation"))
optional("com.fasterxml.jackson.core:jackson-databind")
optional("org.springframework.security:spring-security-core")
testFixturesApi(testFixtures(project(":module:spring-boot-actuator")))
@@ -57,6 +57,5 @@ dependencies {
testImplementation("org.aspectj:aspectjweaver")
testRuntimeOnly("ch.qos.logback:logback-classic")
testRuntimeOnly("com.fasterxml.jackson.core:jackson-databind")
testRuntimeOnly("jakarta.servlet:jakarta.servlet-api")
}
@@ -24,7 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import com.fasterxml.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
@@ -41,7 +41,7 @@ import org.springframework.boot.actuate.endpoint.EndpointAccessResolver;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.boot.actuate.endpoint.jackson.EndpointJsonMapper;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@@ -66,6 +66,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.codec.EncoderHttpMessageWriter;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.JacksonJsonEncoder;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.util.MimeType;
import org.springframework.util.StringUtils;
@@ -141,29 +142,28 @@ public class WebFluxEndpointManagementContextConfiguration {
}
@Bean
@ConditionalOnBean(EndpointObjectMapper.class)
@ConditionalOnBean(EndpointJsonMapper.class)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor serverCodecConfigurerEndpointObjectMapperBeanPostProcessor(
ObjectProvider<EndpointObjectMapper> endpointObjectMapper) {
return new ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor(
SingletonSupplier.of(endpointObjectMapper::getObject));
static ServerCodecConfigurerEndpointJsonMapperBeanPostProcessor serverCodecConfigurerEndpointJsonMapperBeanPostProcessor(
ObjectProvider<EndpointJsonMapper> endpointJsonMapper) {
return new ServerCodecConfigurerEndpointJsonMapperBeanPostProcessor(
SingletonSupplier.of(endpointJsonMapper::getObject));
}
/**
* {@link BeanPostProcessor} to apply {@link EndpointObjectMapper} for
* {@link BeanPostProcessor} to apply {@link EndpointJsonMapper} for
* {@link OperationResponseBody} to
* {@link org.springframework.http.codec.json.Jackson2JsonEncoder} instances.
*/
static class ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor implements BeanPostProcessor {
static class ServerCodecConfigurerEndpointJsonMapperBeanPostProcessor implements BeanPostProcessor {
private static final List<MediaType> MEDIA_TYPES = Collections
.unmodifiableList(Arrays.asList(MediaType.APPLICATION_JSON, new MediaType("application", "*+json")));
private final Supplier<EndpointObjectMapper> endpointObjectMapper;
private final Supplier<EndpointJsonMapper> endpointJsonMapper;
ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor(
Supplier<EndpointObjectMapper> endpointObjectMapper) {
this.endpointObjectMapper = endpointObjectMapper;
ServerCodecConfigurerEndpointJsonMapperBeanPostProcessor(Supplier<EndpointJsonMapper> endpointJsonMapper) {
this.endpointJsonMapper = endpointJsonMapper;
}
@Override
@@ -182,19 +182,17 @@ public class WebFluxEndpointManagementContextConfiguration {
}
}
@SuppressWarnings({ "removal", "deprecation" })
private void process(Encoder<?> encoder) {
if (encoder instanceof org.springframework.http.codec.json.Jackson2JsonEncoder jackson2JsonEncoder) {
this.endpointObjectMapper.get()
if (encoder instanceof JacksonJsonEncoder jacksonJsonEncoder) {
this.endpointJsonMapper.get()
.getSupportedTypes()
.forEach((type) -> jackson2JsonEncoder.registerObjectMappersForType(type,
this::registerForAllMimeTypes));
.forEach((type) -> jacksonJsonEncoder.registerMappersForType(type, this::registerForAllMimeTypes));
}
}
private void registerForAllMimeTypes(Map<MimeType, ObjectMapper> registrar) {
ObjectMapper objectMapper = this.endpointObjectMapper.get().get();
MEDIA_TYPES.forEach((mimeType) -> registrar.put(mimeType, objectMapper));
private void registerForAllMimeTypes(Map<MimeType, JsonMapper> registrar) {
JsonMapper jsonMapper = this.endpointJsonMapper.get().get();
MEDIA_TYPES.forEach((mimeType) -> registrar.put(mimeType, jsonMapper));
}
}
@@ -16,22 +16,21 @@
package org.springframework.boot.webflux.autoconfigure.actuate.web;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.jsontype.TypeSerializer;
import tools.jackson.databind.module.SimpleModule;
import tools.jackson.databind.ser.std.StdScalarSerializer;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.boot.actuate.endpoint.jackson.EndpointJsonMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.http.codec.autoconfigure.CodecsAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
@@ -87,8 +86,8 @@ class WebFluxEndpointIntegrationTests {
}
@Test
void endpointObjectMapperCanBeApplied() {
this.contextRunner.withUserConfiguration(EndpointObjectMapperConfiguration.class)
void endpointJsonMapperCanBeApplied() {
this.contextRunner.withUserConfiguration(EndpointJsonMapperConfiguration.class)
.withPropertyValues("management.endpoints.web.exposure.include:*")
.run((context) -> {
WebTestClient client = createWebTestClient(context);
@@ -140,17 +139,14 @@ class WebFluxEndpointIntegrationTests {
}
@Configuration
@SuppressWarnings({ "deprecation", "removal" })
static class EndpointObjectMapperConfiguration {
static class EndpointJsonMapperConfiguration {
@Bean
EndpointObjectMapper endpointObjectMapper() {
EndpointJsonMapper endpointJsonMapper() {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new ReverseStringSerializer());
ObjectMapper objectMapper = org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.json()
.modules(module)
.build();
return () -> objectMapper;
JsonMapper jsonMapper = JsonMapper.builder().addModule(module).build();
return () -> jsonMapper;
}
static class ReverseStringSerializer extends StdScalarSerializer<Object> {
@@ -160,22 +156,22 @@ class WebFluxEndpointIntegrationTests {
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
public boolean isEmpty(SerializationContext context, Object value) {
return ((String) value).isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
public void serialize(Object value, JsonGenerator gen, SerializationContext context) {
serialize(value, gen);
}
@Override
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException {
public final void serializeWithType(Object value, JsonGenerator gen, SerializationContext context,
TypeSerializer typeSer) {
serialize(value, gen);
}
private void serialize(Object value, JsonGenerator gen) throws IOException {
private void serialize(Object value, JsonGenerator gen) {
StringBuilder builder = new StringBuilder((String) value);
gen.writeString(builder.reverse().toString());
}