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 <mvc:annotation-driven />. Issue: SPR-10768
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<ObjectMapper>, BeanClassLoaderAware, InitializingBean {
|
||||
public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, BeanClassLoaderAware,
|
||||
ApplicationContextAware, InitializingBean {
|
||||
|
||||
private final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
|
||||
|
||||
@@ -336,6 +342,16 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
|
||||
this.builder.findModulesViaServiceLoader(findModules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the construction of Jackson handlers ({@link JsonSerializer}, {@link JsonDeserializer},
|
||||
* {@link KeyDeserializer}, {@code TypeResolverBuilder} and {@code TypeIdResolver}).
|
||||
* @since 4.1.3
|
||||
* @see Jackson2ObjectMapperFactoryBean#setApplicationContext(ApplicationContext)
|
||||
*/
|
||||
public void setHandlerInstantiator(HandlerInstantiator handlerInstantiator) {
|
||||
this.builder.handlerInstantiator(handlerInstantiator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.builder.moduleClassLoader(beanClassLoader);
|
||||
@@ -353,6 +369,17 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the builder {@link ApplicationContext} in order to autowire Jackson handlers ({@link JsonSerializer},
|
||||
* {@link JsonDeserializer}, {@link KeyDeserializer}, {@code TypeResolverBuilder} and {@code TypeIdResolver}).
|
||||
* @since 4.1.3
|
||||
* @see Jackson2ObjectMapperBuilder#applicationContext(ApplicationContext)
|
||||
* @see SpringHandlerInstantiator
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.builder.applicationContext(applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the singleton ObjectMapper.
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.KeyDeserializer;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
|
||||
import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
||||
import com.fasterxml.jackson.databind.introspect.Annotated;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Eventually get Jackson handler ({@link JsonSerializer}, {@link JsonDeserializer},
|
||||
* {@link KeyDeserializer}, {@link TypeResolverBuilder}, {@link TypeIdResolver}) beans by
|
||||
* type from Spring {@link ApplicationContext}. If no bean is found, the default behavior
|
||||
* happen (calling no-argument constructor via reflection).
|
||||
*
|
||||
* @since 4.1.3
|
||||
* @author Sebastien Deleuze
|
||||
* @see Jackson2ObjectMapperBuilder#handlerInstantiator(HandlerInstantiator)
|
||||
* @see HandlerInstantiator
|
||||
*/
|
||||
public class SpringHandlerInstantiator extends HandlerInstantiator {
|
||||
|
||||
private final AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SpringHandlerInstantiator for the given BeanFactory.
|
||||
* @param beanFactory the target BeanFactory
|
||||
*/
|
||||
public SpringHandlerInstantiator(AutowireCapableBeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user