From 2fccf3ff4409a641c20947e8fca90308f7236c64 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Thu, 4 Dec 2014 18:40:49 +0100 Subject: [PATCH] Add support for autowiring Jackson handlers This commit introduces the SpringHandlerInstantiator class, a Jackson HandlerInstantiator that allows to autowire Jackson handlers (JsonSerializer, JsonDeserializer, KeyDeserializer, TypeResolverBuilder and TypeIdResolver) if needed. SpringHandlerInstantiator is automatically used with @EnableWebMvc and . Issue: SPR-10768 --- .../json/Jackson2ObjectMapperBuilder.java | 35 +++ .../json/Jackson2ObjectMapperFactoryBean.java | 29 +- .../json/SpringHandlerInstantiator.java | 88 ++++++ .../json/SpringHandlerInstantiatorTests.java | 272 ++++++++++++++++++ .../AnnotationDrivenBeanDefinitionParser.java | 21 +- .../WebMvcConfigurationSupport.java | 9 +- .../MessageBrokerBeanDefinitionParser.java | 11 +- ...cketMessageBrokerConfigurationSupport.java | 3 +- 8 files changed, 459 insertions(+), 9 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java create mode 100644 spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index 6ae0155d02..f975efdb70 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -31,16 +31,20 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.KeyDeserializer; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import javafx.application.Application; import org.springframework.beans.BeanUtils; import org.springframework.beans.FatalBeanException; +import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -94,6 +98,10 @@ public class Jackson2ObjectMapperBuilder { private ClassLoader moduleClassLoader = getClass().getClassLoader(); + private HandlerInstantiator handlerInstantiator; + + private ApplicationContext applicationContext; + /** * If set to {@code true}, an {@link XmlMapper} will be created using its @@ -379,6 +387,27 @@ public class Jackson2ObjectMapperBuilder { return this; } + /** + * Customize the construction of Jackson handlers ({@link JsonSerializer}, {@link JsonDeserializer}, + * {@link KeyDeserializer}, {@code TypeResolverBuilder} and {@code TypeIdResolver}). + * @since 4.1.3 + * @see Jackson2ObjectMapperBuilder#applicationContext(ApplicationContext) + */ + public Jackson2ObjectMapperBuilder handlerInstantiator(HandlerInstantiator handlerInstantiator) { + this.handlerInstantiator = handlerInstantiator; + return this; + } + + /** + * Set the Spring {@link ApplicationContext} in order to autowire Jackson handlers ({@link JsonSerializer}, + * {@link JsonDeserializer}, {@link KeyDeserializer}, {@code TypeResolverBuilder} and {@code TypeIdResolver}). + * @since 4.1.3 + * @see SpringHandlerInstantiator + */ + public Jackson2ObjectMapperBuilder applicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + return this; + } /** * Build a new {@link ObjectMapper} instance. @@ -468,6 +497,12 @@ public class Jackson2ObjectMapperBuilder { for (Class target : this.mixIns.keySet()) { objectMapper.addMixInAnnotations(target, this.mixIns.get(target)); } + if (this.handlerInstantiator != null) { + objectMapper.setHandlerInstantiator(this.handlerInstantiator); + } + else if (this.applicationContext != null) { + objectMapper.setHandlerInstantiator(new SpringHandlerInstantiator(this.applicationContext.getAutowireCapableBeanFactory())); + } } // Any change to this method should be also applied to spring-jms and spring-messaging diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java index 726756c75f..bdca6e41bb 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java @@ -26,16 +26,20 @@ import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.KeyDeserializer; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; /** * A {@link FactoryBean} for creating a Jackson 2.x {@link ObjectMapper} (default) or @@ -119,9 +123,11 @@ import org.springframework.beans.factory.InitializingBean; * @author Brian Clozel * @author Juergen Hoeller * @author Tadaya Tsuyukubo + * @author Sebastien Deleuze * @since 3.2 */ -public class Jackson2ObjectMapperFactoryBean implements FactoryBean, BeanClassLoaderAware, InitializingBean { +public class Jackson2ObjectMapperFactoryBean implements FactoryBean, BeanClassLoaderAware, + ApplicationContextAware, InitializingBean { private final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); @@ -336,6 +342,16 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean serializerInstance(SerializationConfig config, + Annotated annotated, Class keyDeserClass) { + return (JsonSerializer) this.beanFactory.createBean(keyDeserClass); + } + + @Override + public JsonDeserializer deserializerInstance(DeserializationConfig config, + Annotated annotated, Class deserClass) { + return (JsonDeserializer) this.beanFactory.createBean(deserClass); + } + + @Override + public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, + Annotated annotated, Class serClass) { + return (KeyDeserializer) this.beanFactory.createBean(serClass); + } + + @Override + public TypeResolverBuilder typeResolverBuilderInstance(MapperConfig config, + Annotated annotated, Class resolverClass) { + return (TypeResolverBuilder) this.beanFactory.createBean(resolverClass); + } + + @Override + public TypeIdResolver typeIdResolverInstance(MapperConfig config, + Annotated annotated, Class resolverClass) { + return (TypeIdResolver) this.beanFactory.createBean(resolverClass); + } +} diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java new file mode 100644 index 0000000000..71435a1f4e --- /dev/null +++ b/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java @@ -0,0 +1,272 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.http.converter.json; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; +import com.fasterxml.jackson.databind.annotation.JsonTypeResolver; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; +import com.fasterxml.jackson.databind.type.TypeFactory; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; + +/** + * Test class for {@link SpringHandlerInstantiatorTests}. + * + * @author Sebastien Deleuze + */ +public class SpringHandlerInstantiatorTests { + + private SpringHandlerInstantiator instantiator; + private ObjectMapper objectMapper; + + @Before + public void setup() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("capitalizer", new RootBeanDefinition(Capitalizer.class)); + instantiator = new SpringHandlerInstantiator(bf); + objectMapper = Jackson2ObjectMapperBuilder.json().handlerInstantiator(instantiator).build(); + } + + @Test + public void autowiredSerializer() throws JsonProcessingException { + User user = new User("bob"); + String json = this.objectMapper.writeValueAsString(user); + assertEquals("{\"username\":\"BOB\"}", json); + + } + + @Test + public void autowiredDeserializer() throws IOException { + String json = "{\"username\":\"bob\"}"; + User user = this.objectMapper.readValue(json, User.class); + assertEquals(user.getUsername(), "BOB"); + } + + @Test + public void autowiredKeyDeserializer() throws IOException { + String json = "{\"credentials\":{\"bob\":\"admin\"}}"; + SecurityRegistry registry = this.objectMapper.readValue(json, SecurityRegistry.class); + assertTrue(registry.getCredentials().keySet().contains("BOB")); + assertFalse(registry.getCredentials().keySet().contains("bob")); + } + + @Test + public void applicationContextAwaretypeResolverBuilder() throws JsonProcessingException { + this.objectMapper.writeValueAsString(new Group("authors")); + assertTrue(CustomTypeResolverBuilder.isAutowiredFiledInitialized); + } + + @Test + public void applicationContextAwareTypeIdResolver() throws JsonProcessingException { + this.objectMapper.writeValueAsString(new Group("authors")); + assertTrue(CustomTypeIdResolver.isAutowiredFiledInitialized); + } + + public static class UserDeserializer extends JsonDeserializer { + + @Autowired + private Capitalizer capitalizer; + + @Override + public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + return new User(this.capitalizer.capitalize(node.get("username").asText())); + } + + } + + public static class UserSerializer extends JsonSerializer { + + @Autowired + private Capitalizer capitalizer; + + @Override + public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("username", this.capitalizer.capitalize(user.getUsername())); + jsonGenerator.writeEndObject(); + } + } + + public static class UpperCaseKeyDeserializer extends KeyDeserializer { + + @Autowired + private Capitalizer capitalizer; + + @Override + public Object deserializeKey(String key, DeserializationContext context) throws IOException, JsonProcessingException { + return this.capitalizer.capitalize(key); + } + } + + public static class CustomTypeResolverBuilder extends StdTypeResolverBuilder { + + @Autowired + private Capitalizer capitalizer; + + public static boolean isAutowiredFiledInitialized = false; + + @Override + public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection subtypes) { + isAutowiredFiledInitialized = (this.capitalizer != null); + return super.buildTypeSerializer(config, baseType, subtypes); + } + + @Override + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection subtypes) { + return super.buildTypeDeserializer(config, baseType, subtypes); + } + } + + public static class CustomTypeIdResolver implements TypeIdResolver { + + @Autowired + private Capitalizer capitalizer; + + public static boolean isAutowiredFiledInitialized = false; + + public CustomTypeIdResolver() { + + } + + @Override + public String idFromValueAndType(Object o, Class type) { + return type.getClass().getName(); + } + + @Override + public JsonTypeInfo.Id getMechanism() { + return JsonTypeInfo.Id.CUSTOM; + } + + @Override + public JavaType typeFromId(String s) { + return TypeFactory.defaultInstance().constructFromCanonical(s); + } + + @Override + public String idFromValue(Object value) { + isAutowiredFiledInitialized = (this.capitalizer != null); + return value.getClass().getName(); + } + + @Override + public void init(JavaType type) { + + } + + @Override + public String idFromBaseType() { + return null; + } + } + + @JsonDeserialize(using = UserDeserializer.class) + @JsonSerialize(using = UserSerializer.class) + public static class User { + + private String username; + + public User() { + } + + public User(String username) { + this.username = username; + } + + public String getUsername() { return this.username; } + } + + public static class SecurityRegistry { + + @JsonDeserialize(keyUsing = UpperCaseKeyDeserializer.class) + private Map credentials = new HashMap<>(); + + public void addCredential(String username, String credential) { + this.credentials.put(username, credential); + } + + public Map getCredentials() { + return credentials; + } + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type") + @JsonTypeResolver(CustomTypeResolverBuilder.class) + @JsonTypeIdResolver(CustomTypeIdResolver.class) + public static class Group { + + private String name; + + public Group(String name) { + this.name = name; + } + + public Group() { + + } + + public String getType() { + return Group.class.getName(); + } + } + + public static class Capitalizer { + + public String capitalize(String text) { + return text.toUpperCase(); + } + } +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java index 9bafb340f9..588a64c3e6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; +import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; @@ -44,6 +45,7 @@ import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter; import org.springframework.http.converter.feed.RssChannelHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; @@ -534,14 +536,21 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { } if (jackson2XmlPresent) { - messageConverters.add(createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source)); + RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source); + GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source); + jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true); + jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef); + messageConverters.add(jacksonConverterDef); } else if (jaxb2Present) { messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source)); } if (jackson2Present) { - messageConverters.add(createConverterDefinition(MappingJackson2HttpMessageConverter.class, source)); + RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source); + GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source); + jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef); + messageConverters.add(jacksonConverterDef); } else if (gsonPresent) { messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source)); @@ -550,6 +559,14 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { return messageConverters; } + private GenericBeanDefinition createObjectMapperFactoryDefinition(Object source) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(Jackson2ObjectMapperFactoryBean.class); + beanDefinition.setSource(source); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + return beanDefinition; + } + private RootBeanDefinition createConverterDefinition(Class converterClass, Object source) { RootBeanDefinition beanDefinition = new RootBeanDefinition(converterClass); beanDefinition.setSource(source); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 31b31b2f1e..5f2c4e5a16 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -25,6 +25,8 @@ import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.xml.transform.Source; +import com.fasterxml.jackson.databind.ObjectMapper; + import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanInitializationException; @@ -45,6 +47,7 @@ import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter; import org.springframework.http.converter.feed.RssChannelHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; @@ -671,14 +674,16 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv } if (jackson2XmlPresent) { - messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); + ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build(); + messageConverters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper)); } else if (jaxb2Present) { messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if (jackson2Present) { - messageConverters.add(new MappingJackson2HttpMessageConverter()); + ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build(); + messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper)); } else if (gsonPresent) { messageConverters.add(new GsonHttpMessageConverter()); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java index 9784018336..068e75a778 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java @@ -21,7 +21,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean; import org.springframework.messaging.support.ImmutableMessageChannelInterceptor; import org.w3c.dom.Element; @@ -386,8 +387,12 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser { RootBeanDefinition resolverDef = new RootBeanDefinition(DefaultContentTypeResolver.class); resolverDef.getPropertyValues().add("defaultMimeType", MimeTypeUtils.APPLICATION_JSON); jacksonConverterDef.getPropertyValues().add("contentTypeResolver", resolverDef); - // Use Jackson builder in order to have JSR-310 and Joda-Time modules registered automatically - jacksonConverterDef.getPropertyValues().add("objectMapper", Jackson2ObjectMapperBuilder.json().build()); + // Use Jackson factory in order to have JSR-310 and Joda-Time modules registered automatically + GenericBeanDefinition jacksonFactoryDef = new GenericBeanDefinition(); + jacksonFactoryDef.setBeanClass(Jackson2ObjectMapperFactoryBean.class); + jacksonFactoryDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + jacksonFactoryDef.setSource(source); + jacksonConverterDef.getPropertyValues().add("objectMapper", jacksonFactoryDef); converters.add(jacksonConverterDef); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebSocketMessageBrokerConfigurationSupport.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebSocketMessageBrokerConfigurationSupport.java index cc4eff6b79..df93f746f7 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebSocketMessageBrokerConfigurationSupport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebSocketMessageBrokerConfigurationSupport.java @@ -138,7 +138,8 @@ public abstract class WebSocketMessageBrokerConfigurationSupport extends Abstrac protected MappingJackson2MessageConverter createJacksonConverter() { MappingJackson2MessageConverter messageConverter = super.createJacksonConverter(); // Use Jackson builder in order to have JSR-310 and Joda-Time modules registered automatically - messageConverter.setObjectMapper(Jackson2ObjectMapperBuilder.json().build()); + messageConverter.setObjectMapper(Jackson2ObjectMapperBuilder.json() + .applicationContext(this.getApplicationContext()).build()); return messageConverter; } -- GitLab