diff --git a/src/main/java/org/springframework/data/convert/CustomConversions.java b/src/main/java/org/springframework/data/convert/CustomConversions.java index 1713772a8..899bc6cb1 100644 --- a/src/main/java/org/springframework/data/convert/CustomConversions.java +++ b/src/main/java/org/springframework/data/convert/CustomConversions.java @@ -70,7 +70,7 @@ import org.springframework.util.ObjectUtils; */ public class CustomConversions { - private static final Log logger = LogFactory.getLog(CustomConversions.class); + private static Log logger = LogFactory.getLog(CustomConversions.class); private static final String READ_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as reading converter although it doesn't convert from a store-supported type; You might want to check your annotation setup at the converter implementation"; private static final String WRITE_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as writing converter although it doesn't convert to a store-supported type; You might want to check your annotation setup at the converter implementation"; private static final String NOT_A_CONVERTER = "Converter %s is neither a Spring Converter, GenericConverter or ConverterFactory"; @@ -324,7 +324,8 @@ public class CustomConversions { readingPairs.add(pair); - if (logger.isWarnEnabled() && !converterRegistration.isSimpleSourceType()) { + if (logger.isWarnEnabled() && !converterRegistration.isSimpleSourceType() + && !Collection.class.isAssignableFrom(pair.getSourceType())) { logger.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType())); } } @@ -334,7 +335,8 @@ public class CustomConversions { writingPairs.add(pair); customSimpleTypes.add(pair.getSourceType()); - if (logger.isWarnEnabled() && !converterRegistration.isSimpleTargetType()) { + if (logger.isWarnEnabled() && !converterRegistration.isSimpleTargetType() + && !Collection.class.isAssignableFrom(pair.getTargetType())) { logger.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType())); } } diff --git a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java index 9891c27c7..5f4b49c44 100644 --- a/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java +++ b/src/test/java/org/springframework/data/convert/CustomConversionsUnitTests.java @@ -27,9 +27,11 @@ import java.util.Locale; import java.util.Map; import java.util.function.Predicate; +import org.apache.commons.logging.Log; import org.jmolecules.ddd.types.Association; import org.jmolecules.ddd.types.Identifier; import org.junit.jupiter.api.Test; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; @@ -44,6 +46,7 @@ import org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConv import org.springframework.data.geo.Point; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.test.util.ReflectionTestUtils; /** * Unit tests for {@link CustomConversions}. @@ -156,8 +159,7 @@ class CustomConversionsUnitTests { var conversions = StoreConversions.of(new SimpleTypeHolder(Collections.singleton(Format.class), true)); - var customConversions = new CustomConversions(conversions, - Collections.singletonList(new FormatConverterFactory())); + var customConversions = new CustomConversions(conversions, Collections.singletonList(new FormatConverterFactory())); assertThat(customConversions.getCustomWriteTarget(String.class, SimpleDateFormat.class)).isPresent(); } @@ -214,7 +216,7 @@ class CustomConversionsUnitTests { new CustomConversions(StoreConversions.of(DATE_EXCLUDING_SIMPLE_TYPE_HOLDER), Collections.singletonList(Jsr310Converters.LocalDateTimeToInstantConverter.INSTANCE)) - .registerConvertersIn(registry); + .registerConvertersIn(registry); verify(registry).addConverter(any(Jsr310Converters.LocalDateTimeToInstantConverter.class)); } @@ -293,8 +295,8 @@ class CustomConversionsUnitTests { @Test void hasValueConverterReturnsFalseWhenNoPropertyValueConversionsAreConfigured() { - ConverterConfiguration configuration = new ConverterConfiguration(StoreConversions.NONE, - Collections.emptyList(), it -> true, null); + ConverterConfiguration configuration = new ConverterConfiguration(StoreConversions.NONE, Collections.emptyList(), + it -> true, null); CustomConversions conversions = new CustomConversions(configuration); @@ -315,8 +317,8 @@ class CustomConversionsUnitTests { doReturn(true).when(mockPropertyValueConversions).hasValueConverter(eq(mockProperty)); - ConverterConfiguration configuration = new ConverterConfiguration(StoreConversions.NONE, - Collections.emptyList(), it -> true, mockPropertyValueConversions); + ConverterConfiguration configuration = new ConverterConfiguration(StoreConversions.NONE, Collections.emptyList(), + it -> true, mockPropertyValueConversions); CustomConversions conversions = new CustomConversions(configuration); @@ -328,6 +330,19 @@ class CustomConversionsUnitTests { verifyNoInteractions(mockProperty); } + @Test // GH-3306 + void doesNotWarnForAsymmetricListConverter() { + + Log actualLogger = (Log) ReflectionTestUtils.getField(CustomConversions.class, "logger"); + Log actualLoggerSpy = spy(actualLogger); + ReflectionTestUtils.setField(CustomConversions.class, "logger", actualLoggerSpy, Log.class); + + new CustomConversions(StoreConversions.NONE, List.of(ListOfNumberToStringConverter.INSTANCE)); + + verify(actualLoggerSpy, never()).warn(anyString()); + verify(actualLoggerSpy, never()).warn(anyString(), any()); + } + private static Class createProxyTypeFor(Class type) { var factory = new ProxyFactory(); @@ -337,6 +352,16 @@ class CustomConversionsUnitTests { return factory.getProxy().getClass(); } + @ReadingConverter + enum ListOfNumberToStringConverter implements Converter, String> { + + INSTANCE; + + public String convert(List source) { + return source.toString(); + } + } + enum FormatToStringConverter implements Converter { INSTANCE;