#460 - HalHandlerInstantiator now uses a BeanFactory for all non pre-registered types.

The HalHandlerInstantiator now accepts an AutowireCapableBeanFactory to delegate component creation requests to for non pre-build serializers. This allows other serializers to benefit from Spring instantiation but at the same time use the dedicated serializers created as singletons.
This commit is contained in:
Oliver Gierke
2016-06-09 17:20:27 +02:00
parent 01084c5fbd
commit 0331b8d2c7
3 changed files with 92 additions and 38 deletions

View File

@@ -29,6 +29,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -60,6 +61,7 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.plugin.core.support.PluginRegistryFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
@@ -107,9 +109,8 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
if (JSONPATH_PRESENT) {
AbstractBeanDefinition linkDiscovererBeanDefinition = getLinkDiscovererBeanDefinition(type);
registerBeanDefinition(
new BeanDefinitionHolder(linkDiscovererBeanDefinition, BeanDefinitionReaderUtils.generateBeanName(
linkDiscovererBeanDefinition, registry)), registry);
registerBeanDefinition(new BeanDefinitionHolder(linkDiscovererBeanDefinition,
BeanDefinitionReaderUtils.generateBeanName(linkDiscovererBeanDefinition, registry)), registry);
}
}
@@ -224,7 +225,7 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
*/
static class Jackson2ModuleRegisteringBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;
private AutowireCapableBeanFactory beanFactory;
/*
* (non-Javadoc)
@@ -232,7 +233,11 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
Assert.isInstanceOf(AutowireCapableBeanFactory.class, beanFactory,
"BeanFactory must be an AutowireCapableBeanFactory!");
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
/*
@@ -251,8 +256,8 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
if (bean instanceof AnnotationMethodHandlerAdapter) {
AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
List<HttpMessageConverter<?>> augmentedConverters = potentiallyRegisterModule(Arrays.asList(adapter
.getMessageConverters()));
List<HttpMessageConverter<?>> augmentedConverters = potentiallyRegisterModule(
Arrays.asList(adapter.getMessageConverters()));
adapter
.setMessageConverters(augmentedConverters.toArray(new HttpMessageConverter<?>[augmentedConverters.size()]));
}
@@ -294,8 +299,8 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe
MessageSourceAccessor.class);
halObjectMapper.registerModule(new Jackson2HalModule());
halObjectMapper.setHandlerInstantiator(
new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider, linkRelationMessageSource));
halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider,
linkRelationMessageSource, beanFactory));
MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
ResourceSupport.class);

View File

@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.hateoas.Link;
@@ -121,22 +122,21 @@ public class Jackson2HalModule extends SimpleModule {
private final BeanProperty property;
private final CurieProvider curieProvider;
private final EmbeddedMapper mapper;
private final MessageSourceAccessor messageSource;
private final MessageSourceAccessor accessor;
public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper,
MessageSourceAccessor messageSource) {
this(null, curieProvider, mapper, messageSource);
public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper, MessageSourceAccessor accessor) {
this(null, curieProvider, mapper, accessor);
}
public HalLinkListSerializer(BeanProperty property, CurieProvider curieProvider, EmbeddedMapper mapper,
MessageSourceAccessor messageSource) {
MessageSourceAccessor accessor) {
super(TypeFactory.defaultInstance().constructType(List.class));
this.property = property;
this.curieProvider = curieProvider;
this.mapper = mapper;
this.messageSource = messageSource;
this.accessor = accessor;
}
/*
@@ -204,7 +204,7 @@ public class Jackson2HalModule extends SimpleModule {
}
/**
* Wraps the given link into a HAL specifc extension.
* Wraps the given link into a HAL specific extension.
*
* @param link must not be {@literal null}.
* @return
@@ -233,8 +233,7 @@ public class Jackson2HalModule extends SimpleModule {
Assert.hasText(localRel, "Local relation must not be null or empty!");
try {
return messageSource == null ? null
: messageSource.getMessage(String.format(RELATION_MESSAGE_TEMPLATE, localRel));
return accessor == null ? null : accessor.getMessage(String.format(RELATION_MESSAGE_TEMPLATE, localRel));
} catch (NoSuchMessageException o_O) {
return null;
}
@@ -247,7 +246,7 @@ public class Jackson2HalModule extends SimpleModule {
@Override
public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
throws JsonMappingException {
return new HalLinkListSerializer(property, curieProvider, mapper, messageSource);
return new HalLinkListSerializer(property, curieProvider, mapper, accessor);
}
/*
@@ -681,30 +680,73 @@ public class Jackson2HalModule extends SimpleModule {
}
}
/**
* HandlerInstantiator to create HAL-specific serializers, deserializers etc.
*
* @author Oliver Gierke
*/
public static class HalHandlerInstantiator extends HandlerInstantiator {
private final Map<Class<?>, Object> instanceMap = new HashMap<Class<?>, Object>();
private final Map<Class<?>, Object> serializers = new HashMap<Class<?>, Object>();
private final AutowireCapableBeanFactory delegate;
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider,
MessageSourceAccessor messageSource) {
this(resolver, curieProvider, messageSource, true);
/**
* Creates a new {@link HalHandlerInstantiator} using the given {@link RelProvider}, {@link CurieProvider} and
* {@link MessageSourceAccessor} and {@link AutowireCapableBeanFactory}. Registers a prepared
* {@link HalResourcesSerializer} and {@link HalLinkListSerializer} falling back to instantiation using the given
* {@link AutowireCapableBeanFactory} if provided, or simple default constructor instantiation if not.
*
* @param provider must not be {@literal null}.
* @param curieProvider can be {@literal null}.
* @param accessor can be {@literal null}.
* @param beanFactory can be {@literal null}
*/
public HalHandlerInstantiator(RelProvider provider, CurieProvider curieProvider, MessageSourceAccessor accessor,
AutowireCapableBeanFactory beanFactory) {
this(provider, curieProvider, accessor, true, beanFactory);
}
public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider,
MessageSourceAccessor messageSource, boolean enforceEmbeddedCollections) {
EmbeddedMapper mapper = new EmbeddedMapper(resolver, curieProvider, enforceEmbeddedCollections);
Assert.notNull(resolver, "RelProvider must not be null!");
this.instanceMap.put(HalResourcesSerializer.class, new HalResourcesSerializer(mapper));
this.instanceMap.put(HalLinkListSerializer.class,
new HalLinkListSerializer(curieProvider, mapper, messageSource));
/**
* Creates a new {@link HalHandlerInstantiator} using the given {@link RelProvider}, {@link CurieProvider} and
* {@link MessageSourceAccessor}. Registers a prepared {@link HalResourcesSerializer} and
* {@link HalLinkListSerializer} falling back to instantiation expecting a default constructor.
*
* @param provider must not be {@literal null}.
* @param curieProvider can be {@literal null}.
* @param messageSourceAccessor can be {@literal null}.
*/
public HalHandlerInstantiator(RelProvider provider, CurieProvider curieProvider,
MessageSourceAccessor messageSourceAccessor) {
this(provider, curieProvider, messageSourceAccessor, true);
}
private Object findInstance(Class<?> type) {
/**
* Creates a new {@link HalHandlerInstantiator} using the given {@link RelProvider}, {@link CurieProvider} and
* {@link MessageSourceAccessor} and whether to enforce embedded collections. Registers a prepared
* {@link HalResourcesSerializer} and {@link HalLinkListSerializer} falling back to instantiation expecting a
* default constructor.
*
* @param provider must not be {@literal null}.
* @param curieProvider can be {@literal null}
* @param accessor can be {@literal null}.
* @param enforceEmbeddedCollections
*/
public HalHandlerInstantiator(RelProvider provider, CurieProvider curieProvider, MessageSourceAccessor accessor,
boolean enforceEmbeddedCollections) {
this(provider, curieProvider, accessor, enforceEmbeddedCollections, null);
}
Object result = instanceMap.get(type);
return result != null ? result : BeanUtils.instantiateClass(type);
private HalHandlerInstantiator(RelProvider provider, CurieProvider curieProvider, MessageSourceAccessor accessor,
boolean enforceEmbeddedCollections, AutowireCapableBeanFactory delegate) {
Assert.notNull(provider, "RelProvider must not be null!");
EmbeddedMapper mapper = new EmbeddedMapper(provider, curieProvider, enforceEmbeddedCollections);
this.delegate = delegate;
this.serializers.put(HalResourcesSerializer.class, new HalResourcesSerializer(mapper));
this.serializers.put(HalLinkListSerializer.class, new HalLinkListSerializer(curieProvider, mapper, accessor));
}
/*
@@ -754,6 +796,13 @@ public class Jackson2HalModule extends SimpleModule {
public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, Annotated annotated, Class<?> resolverClass) {
return (TypeIdResolver) findInstance(resolverClass);
}
private Object findInstance(Class<?> type) {
Object result = serializers.get(type);
return result != null ? result : delegate != null ? delegate.createBean(type) : BeanUtils.instantiateClass(type);
}
}
/**