#1159 - HalConfiguration now exposes whether to enforce arrays in embedded clauses.

The flag to control this had been directly configured on the HalHandlerInstantiator which wasn't accessible to users in the first place. Refined the setup and forwarding of the bean factory into HalHandlerInstantiator so that non-Spring-HATEOAS Jackson serializer lookups are routed through the Spring container potentially picking up prototype bean definitions.
This commit is contained in:
Oliver Drotbohm
2019-12-11 22:10:13 +01:00
parent aef81d1e57
commit 5502bfdf85
7 changed files with 33 additions and 48 deletions

View File

@@ -54,6 +54,12 @@ public class HalConfiguration {
*/
private final @Wither @Getter boolean applyPropertyNamingStrategy;
/**
* Configures whether to always use collections for embeddeds, even if there's only one entry for a link relation.
* Defaults to {@literal true}.
*/
private final @Wither @Getter boolean enforceEmbeddedCollections;
/**
* Creates a new default {@link HalConfiguration} rendering single links as immediate sub-document.
*/
@@ -62,6 +68,7 @@ public class HalConfiguration {
this.renderSingleLinks = RenderSingleLinks.AS_SINGLE;
this.singleLinksPerPattern = new LinkedHashMap<>();
this.applyPropertyNamingStrategy = true;
this.enforceEmbeddedCollections = true;
}
/**

View File

@@ -15,10 +15,13 @@
*/
package org.springframework.hateoas.mediatype.hal;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.client.LinkDiscoverer;
@@ -38,27 +41,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Oliver Drotbohm
*/
@Configuration
@RequiredArgsConstructor
public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
private final LinkRelationProvider relProvider;
private final ObjectProvider<CurieProvider> curieProvider;
private final ObjectProvider<HalConfiguration> halConfiguration;
private final @Qualifier("messageResolver") MessageResolver resolver;
/**
* @param relProvider
* @param curieProvider
* @param halConfiguration
* @param resolver
*/
public HalMediaTypeConfiguration(LinkRelationProvider relProvider, ObjectProvider<CurieProvider> curieProvider,
ObjectProvider<HalConfiguration> halConfiguration, MessageResolver resolver) {
this.relProvider = relProvider;
this.curieProvider = curieProvider;
this.halConfiguration = halConfiguration;
this.resolver = resolver;
}
private final AutowireCapableBeanFactory beanFactory;
@Bean
LinkDiscoverer halLinkDisocoverer() {
@@ -85,7 +75,7 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation {
mapper.registerModule(new Jackson2HalModule());
mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider,
curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver,
halConfiguration.getIfAvailable(HalConfiguration::new)));
halConfiguration.getIfAvailable(HalConfiguration::new), beanFactory));
return mapper;
}

View File

@@ -52,7 +52,6 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
@@ -809,33 +808,17 @@ public class Jackson2HalModule extends SimpleModule {
*/
public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, MessageResolver resolver,
HalConfiguration halConfiguration) {
this(provider, curieProvider, resolver, true, halConfiguration);
this(provider, curieProvider, resolver, halConfiguration, null);
}
/**
* Creates a new {@link HalHandlerInstantiator} using the given {@link LinkRelationProvider}, {@link CurieProvider}
* and {@link MessageResolver} 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 resolver must not be {@literal null}..
* @param enforceEmbeddedCollections
*/
public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, MessageResolver resolver,
boolean enforceEmbeddedCollections, HalConfiguration halConfiguration) {
this(provider, curieProvider, resolver, enforceEmbeddedCollections, null, halConfiguration);
}
private HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, MessageResolver resolver,
boolean enforceEmbeddedCollections, @Nullable AutowireCapableBeanFactory delegate,
HalConfiguration halConfiguration) {
HalConfiguration halConfiguration, @Nullable AutowireCapableBeanFactory delegate) {
Assert.notNull(provider, "RelProvider must not be null!");
Assert.notNull(curieProvider, "CurieProvider must not be null!");
EmbeddedMapper mapper = new EmbeddedMapper(provider, curieProvider, enforceEmbeddedCollections);
EmbeddedMapper mapper = new EmbeddedMapper(provider, curieProvider,
halConfiguration.isEnforceEmbeddedCollections());
this.delegate = delegate;

View File

@@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.client.LinkDiscoverer;
@@ -49,6 +50,7 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
private final ObjectProvider<HalFormsConfiguration> halFormsConfiguration;
private final ObjectProvider<HalConfiguration> halConfiguration;
private final MessageResolver resolver;
private final AbstractAutowireCapableBeanFactory beanFactory;
@Bean
LinkDiscoverer halFormsLinkDiscoverer() {
@@ -77,7 +79,7 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation {
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerModule(new Jackson2HalFormsModule());
mapper.setHandlerInstantiator(new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider,
curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver, true, configuration));
curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver, configuration, beanFactory));
return mapper;
}

View File

@@ -164,13 +164,14 @@ public class Jackson2HalFormsModule extends SimpleModule {
private final Map<Class<?>, Object> serializers = new HashMap<>();
public HalFormsHandlerInstantiator(LinkRelationProvider resolver, CurieProvider curieProvider,
MessageResolver accessor, boolean enforceEmbeddedCollections, HalFormsConfiguration configuration) {
MessageResolver accessor, HalFormsConfiguration configuration, AutowireCapableBeanFactory beanFactory) {
super(resolver, curieProvider, accessor, enforceEmbeddedCollections, configuration.getHalConfiguration());
super(resolver, curieProvider, accessor, configuration.getHalConfiguration(), beanFactory);
EmbeddedMapper mapper = new EmbeddedMapper(resolver, curieProvider, enforceEmbeddedCollections);
HalFormsTemplateBuilder builder = new HalFormsTemplateBuilder(configuration, accessor);
HalConfiguration halConfiguration = configuration.getHalConfiguration();
EmbeddedMapper mapper = new EmbeddedMapper(resolver, curieProvider,
halConfiguration.isEnforceEmbeddedCollections());
HalFormsTemplateBuilder builder = new HalFormsTemplateBuilder(configuration, accessor);
this.serializers.put(HalFormsRepresentationModelSerializer.class,
new HalFormsRepresentationModelSerializer(builder));
@@ -182,10 +183,9 @@ public class Jackson2HalFormsModule extends SimpleModule {
}
public HalFormsHandlerInstantiator(LinkRelationProvider relProvider, CurieProvider curieProvider,
MessageResolver resolver, boolean enforceEmbeddedCollections, AutowireCapableBeanFactory beanFactory) {
MessageResolver resolver, AutowireCapableBeanFactory beanFactory) {
this(relProvider, curieProvider, resolver, enforceEmbeddedCollections,
beanFactory.getBean(HalFormsConfiguration.class));
this(relProvider, curieProvider, resolver, beanFactory.getBean(HalFormsConfiguration.class), beanFactory);
}
@Nullable