diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 6b40694c0..72a9606ec 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -18,6 +18,7 @@ package org.springframework.data.redis.serializer; import java.io.IOException; import java.io.Serial; import java.util.Collections; +import java.util.function.Consumer; import java.util.function.Supplier; import org.springframework.cache.support.NullValue; @@ -196,12 +197,22 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer typeHintPropertyName : Lazy.of(() -> defaultTypingEnabled.get() ? null : mapper.getDeserializationConfig().getDefaultTyper(null) - .buildTypeDeserializer(mapper.getDeserializationConfig(), - mapper.getTypeFactory().constructType(Object.class), Collections.emptyList()) + .buildTypeDeserializer(mapper.getDeserializationConfig(), + mapper.getTypeFactory().constructType(Object.class), Collections.emptyList()) .getPropertyName()) .or("@class"); } + /** + * Gets the configured {@link ObjectMapper} used internally by this {@link GenericJackson2JsonRedisSerializer} + * to de/serialize {@link Object objects} as {@literal JSON}. + * + * @return the configured {@link ObjectMapper}. + */ + protected ObjectMapper getObjectMapper() { + return this.mapper; + } + @Override public byte[] serialize(@Nullable Object source) throws SerializationException { @@ -248,11 +259,33 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer objectMapperConfigurer) { + + Assert.notNull(objectMapperConfigurer, + "Consumer used to configure and customize ObjectMapper must not be null"); + + objectMapperConfigurer.accept(getObjectMapper()); + + return this; + } + protected JavaType resolveType(byte[] source, Class type) throws IOException { if (!type.equals(Object.class) || !defaultTypingEnabled.get()) { diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java index 55830ba5b..9f873e09b 100644 --- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java +++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java @@ -17,8 +17,14 @@ package org.springframework.data.redis.serializer; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.springframework.test.util.ReflectionTestUtils.getField; import static org.springframework.util.ObjectUtils.nullSafeEquals; @@ -32,6 +38,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -412,6 +419,33 @@ class GenericJackson2JsonRedisSerializerUnitTests { assertThat(serializer.deserialize(source, WithJsr310.class).myDate).isEqualTo(java.time.LocalDate.of(2022,9,2)); } + @Test // GH-2601 + public void internalObjectMapperCustomization() { + + GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(); + + com.fasterxml.jackson.databind.Module mockModule = mock(com.fasterxml.jackson.databind.Module.class); + + ObjectMapper mockObjectMapper = mock(ObjectMapper.class); + + Consumer configurer = objectMapper -> mockObjectMapper.registerModule(mockModule); + + assertThat(serializer.configure(configurer)).isSameAs(serializer); + + verify(mockObjectMapper, times(1)).registerModule(eq(mockModule)); + verifyNoMoreInteractions(mockObjectMapper); + verifyNoInteractions(mockModule); + } + + @Test // GH-2601 + public void configureWithNullConsumerThrowsIllegalArgumentException() { + + assertThatIllegalArgumentException() + .isThrownBy(() -> new GenericJackson2JsonRedisSerializer().configure(null)) + .withMessage("Consumer used to configure and customize ObjectMapper must not be null") + .withNoCause(); + } + private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) { NullValue nv = BeanUtils.instantiateClass(NullValue.class);