diff --git a/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java b/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java index e5018d218..73c9e7c53 100644 --- a/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java +++ b/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java @@ -17,8 +17,10 @@ package org.springframework.data.web.config; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -156,7 +158,9 @@ public class SpringDataWebConfiguration implements WebMvcConfigurer, BeanClassLo if (ClassUtils.isPresent("com.jayway.jsonpath.DocumentContext", context.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", context.getClassLoader())) { - ProjectingJackson2HttpMessageConverter converter = new ProjectingJackson2HttpMessageConverter(new ObjectMapper()); + ObjectMapper mapper = getUniqueBean(ObjectMapper.class, context, () -> new ObjectMapper()); + + ProjectingJackson2HttpMessageConverter converter = new ProjectingJackson2HttpMessageConverter(mapper); converter.setBeanFactory(context); forwardBeanClassLoader(converter); @@ -193,4 +197,22 @@ public class SpringDataWebConfiguration implements WebMvcConfigurer, BeanClassLo target.setBeanClassLoader(beanClassLoader); } } + + /** + * Returns the uniquely available bean of the given type from the given {@link ApplicationContext} or the one provided + * by the given {@link Supplier} in case the initial lookup fails. + * + * @param type must not be {@literal null}. + * @param context must not be {@literal null}. + * @param fallback must not be {@literal null}. + * @return + */ + private static T getUniqueBean(Class type, ApplicationContext context, Supplier fallback) { + + try { + return context.getBean(type); + } catch (NoSuchBeanDefinitionException o_O) { + return fallback.get(); + } + } } diff --git a/src/test/java/org/springframework/data/web/config/SpringDataWebConfigurationIntegrationTests.java b/src/test/java/org/springframework/data/web/config/SpringDataWebConfigurationIntegrationTests.java index f4205eb13..056db82bb 100644 --- a/src/test/java/org/springframework/data/web/config/SpringDataWebConfigurationIntegrationTests.java +++ b/src/test/java/org/springframework/data/web/config/SpringDataWebConfigurationIntegrationTests.java @@ -18,15 +18,20 @@ package org.springframework.data.web.config; import static org.assertj.core.api.Assertions.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; import org.assertj.core.api.Condition; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.classloadersupport.HidingClassLoader; import org.springframework.data.web.ProjectingJackson2HttpMessageConverter; import org.springframework.data.web.XmlBeamHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.test.util.ReflectionTestUtils; import org.xmlbeam.XBProjector; import com.fasterxml.jackson.databind.ObjectMapper; @@ -46,7 +51,8 @@ public class SpringDataWebConfigurationIntegrationTests { List> converters = new ArrayList>(); - createConfigWithClassLoader(HidingClassLoader.hide(ObjectMapper.class)).extendMessageConverters(converters); + createConfigWithClassLoader(HidingClassLoader.hide(ObjectMapper.class), + it -> it.extendMessageConverters(converters)); assertThat(converters).areNot(instanceWithClassName(ProjectingJackson2HttpMessageConverter.class)); } @@ -56,7 +62,8 @@ public class SpringDataWebConfigurationIntegrationTests { List> converters = new ArrayList>(); - createConfigWithClassLoader(HidingClassLoader.hide(DocumentContext.class)).extendMessageConverters(converters); + createConfigWithClassLoader(HidingClassLoader.hide(DocumentContext.class), + it -> it.extendMessageConverters(converters)); assertThat(converters).areNot(instanceWithClassName(ProjectingJackson2HttpMessageConverter.class)); } @@ -67,7 +74,7 @@ public class SpringDataWebConfigurationIntegrationTests { List> converters = new ArrayList>(); ClassLoader classLoader = HidingClassLoader.hide(XBProjector.class); - createConfigWithClassLoader(classLoader).extendMessageConverters(converters); + createConfigWithClassLoader(classLoader, it -> it.extendMessageConverters(converters)); assertThat(converters).areNot(instanceWithClassName(XmlBeamHttpMessageConverter.class)); } @@ -77,23 +84,41 @@ public class SpringDataWebConfigurationIntegrationTests { List> converters = new ArrayList>(); - createConfigWithClassLoader(getClass().getClassLoader()).extendMessageConverters(converters); + createConfigWithClassLoader(getClass().getClassLoader(), it -> it.extendMessageConverters(converters)); assertThat(converters).haveAtLeastOne(instanceWithClassName(XmlBeamHttpMessageConverter.class)); assertThat(converters).haveAtLeastOne(instanceWithClassName(ProjectingJackson2HttpMessageConverter.class)); } - private SpringDataWebConfiguration createConfigWithClassLoader(ClassLoader classLoader) { + @Test // DATACMNS-1152 + public void usesCustomObjectMapper() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - SpringDataWebConfiguration.class); + createConfigWithClassLoader(getClass().getClassLoader(), it -> { - context.setClassLoader(classLoader); + List> converters = new ArrayList<>(); + it.extendMessageConverters(converters); - try { - return context.getBean(SpringDataWebConfiguration.class); - } finally { - context.close(); + // Converters contains ProjectingJackson2HttpMessageConverter with custom ObjectMapper + assertThat(converters).anySatisfy(converter -> { + assertThat(converter).isInstanceOfSatisfying(ProjectingJackson2HttpMessageConverter.class, __ -> { + assertThat(ReflectionTestUtils.getField(converter, "objectMapper")).isSameAs(SomeConfiguration.MAPPER); + }); + }); + + }, SomeConfiguration.class); + } + + private void createConfigWithClassLoader(ClassLoader classLoader, Consumer callback, + Class... additionalConfigurationClasses) { + + List> configClasses = new ArrayList<>(Arrays.asList(additionalConfigurationClasses)); + configClasses.add(SpringDataWebConfiguration.class); + + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + configClasses.toArray(new Class[configClasses.size()]))) { + + context.setClassLoader(classLoader); + callback.accept(context.getBean(SpringDataWebConfiguration.class)); } } @@ -110,4 +135,15 @@ public class SpringDataWebConfigurationIntegrationTests { return new Condition<>(it -> it.getClass().getName().equals(expectedClass.getName()), // "with class name %s!", expectedClass.getName()); } + + @Configuration + static class SomeConfiguration { + + static ObjectMapper MAPPER = new ObjectMapper(); + + @Bean + ObjectMapper mapper() { + return MAPPER; + } + } }