Skip to content

Commit c51ef63

Browse files
committed
#728 - Fix dependency cycles in WebFlux / WebMVC code
WebHandler is now independent of a particular web stack as the final LinkBuilder creation is externalized through a SAM type that client code now uses to pass a constructor. Refactored the ReactiveLinkBuilder to avoid a dependency on ControllerLinkBuilder (read: the WebMVC stack). With that change, WebHandler is now moved back to the core package. Refactored LinkBuilderSupport to keep a UriComponentsBuilder instance around instead of UriComponents. It creates a defensive copy of the builder instance on state modifying invocations and returns a new LinkBuilderSupport instance. Moved SpringMvcAffordanceBuilder to the core package and removed Mvc segment from its name to make obvious it's not tied to SpringMVC. Introduced TemplateVariableAwareLinkBuilderSupport as common base class between ControllerLinkBuilder and ReactiveLinkBuilder. It extracts the TemplateVariable behavior from ControllerLinkBuilder. Extracted import of web stack specific configuration into dedicated ImportSelector and make use of String based class names to avoid cyclic dependencies between the config packages. Removed BasicLinkBuilder.
1 parent dc084f0 commit c51ef63

24 files changed

+452
-380
lines changed

src/main/java/org/springframework/hateoas/config/EnableHypermediaSupport.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import java.lang.annotation.Retention;
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.lang.annotation.Target;
23-
import java.util.EnumSet;
24-
import java.util.Set;
2523

2624
import org.springframework.context.ApplicationContext;
2725
import org.springframework.context.annotation.Import;
@@ -37,7 +35,7 @@
3735
* <li>{@link LinkDiscoverer}</li>
3836
* <li>a Jackson 2 module to correctly marshal the resource model classes into the appropriate representation.
3937
* </ul>
40-
*
38+
*
4139
* @see LinkDiscoverer
4240
* @see EntityLinks
4341
* @author Oliver Gierke
@@ -47,35 +45,35 @@
4745
@Target(ElementType.TYPE)
4846
@Documented
4947
@EnableEntityLinks
50-
@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class })
48+
@Import({ HypermediaSupportBeanDefinitionRegistrar.class, HateoasConfiguration.class, WebStackImportSelector.class })
5149
public @interface EnableHypermediaSupport {
5250

5351
/**
5452
* The hypermedia type to be supported.
55-
*
53+
*
5654
* @return
5755
*/
5856
HypermediaType[] type();
5957

6058
/**
6159
* Hypermedia representation types supported.
62-
*
60+
*
6361
* @author Oliver Gierke
6462
* @author Greg Turnquist
6563
*/
6664
enum HypermediaType {
6765

6866
/**
6967
* HAL - Hypermedia Application Language.
70-
*
68+
*
7169
* @see http://stateless.co/hal_specification.html
7270
* @see http://tools.ietf.org/html/draft-kelly-json-hal-05
7371
*/
7472
HAL,
7573

7674
/**
7775
* HAL-FORMS - Independent, backward-compatible extension of the HAL designed to add runtime FORM support
78-
*
76+
*
7977
* @see https://rwcbook.github.io/hal-forms/
8078
*/
8179
HAL_FORMS,
@@ -93,7 +91,5 @@ enum HypermediaType {
9391
* @see http://uberhypermedia.org/
9492
*/
9593
UBER;
96-
97-
private static Set<HypermediaType> HAL_BASED_MEDIATYPES = EnumSet.of(HAL, HAL_FORMS);
9894
}
9995
}

src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,16 @@
3535
import org.springframework.hateoas.LinkDiscoverer;
3636
import org.springframework.hateoas.collectionjson.CollectionJsonLinkDiscoverer;
3737
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
38-
import org.springframework.hateoas.config.mvc.WebMvcHateoasConfiguration;
39-
import org.springframework.hateoas.config.reactive.WebFluxHateoasConfiguration;
4038
import org.springframework.hateoas.hal.HalLinkDiscoverer;
4139
import org.springframework.hateoas.hal.forms.HalFormsLinkDiscoverer;
42-
import org.springframework.hateoas.support.WebStack;
4340
import org.springframework.hateoas.uber.UberLinkDiscoverer;
4441
import org.springframework.util.ClassUtils;
4542

4643
/**
4744
* {@link ImportBeanDefinitionRegistrar} implementation to activate hypermedia support based on the configured
4845
* hypermedia type. Activates {@link EntityLinks} support as well (essentially as if {@link EnableEntityLinks} was
4946
* activated as well).
50-
*
47+
*
5148
* @author Oliver Gierke
5249
* @author Greg Turnquist
5350
*/
@@ -70,7 +67,7 @@ public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionR
7067
* collection using the application context.
7168
*/
7269
for (HypermediaType type : types) {
73-
70+
7471
BeanDefinitionBuilder hypermediaTypeBeanDefinition = genericBeanDefinition(HypermediaType.class, () -> type);
7572
registerSourcedBeanDefinition(hypermediaTypeBeanDefinition, metadata, registry);
7673
}
@@ -87,30 +84,11 @@ public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionR
8784
BeanDefinitionReaderUtils.generateBeanName(linkDiscovererBeanDefinition, registry)), registry);
8885
}
8986
}
90-
91-
/*
92-
* Register a Spring MVC-specific HATEOAS configuration.
93-
*/
94-
if (WebStack.WEBMVC.isAvailable()) {
95-
96-
BeanDefinitionBuilder webMvcHateosConfiguration = rootBeanDefinition(WebMvcHateoasConfiguration.class);
97-
registerSourcedBeanDefinition(webMvcHateosConfiguration, metadata, registry);
98-
}
99-
100-
/*
101-
* Register a Spring WebFlux-specific HATEOAS configuration.
102-
*/
103-
if (WebStack.WEBFLUX.isAvailable()) {
104-
105-
BeanDefinitionBuilder webFluxHateoasConfiguration = rootBeanDefinition(WebFluxHateoasConfiguration.class);
106-
registerSourcedBeanDefinition(webFluxHateoasConfiguration, metadata, registry);
107-
}
108-
10987
}
11088

11189
/**
11290
* Returns a {@link LinkDiscoverer} {@link BeanDefinition} suitable for the given {@link HypermediaType}.
113-
*
91+
*
11492
* @param type
11593
* @return
11694
*/
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.hateoas.config;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import org.springframework.context.annotation.ImportSelector;
22+
import org.springframework.core.type.AnnotationMetadata;
23+
import org.springframework.hateoas.support.WebStack;
24+
25+
/**
26+
* {@link ImportSelector} to include web stack specific configuration.
27+
*
28+
* @author Oliver Drotbohm
29+
*/
30+
class WebStackImportSelector implements ImportSelector {
31+
32+
/*
33+
* (non-Javadoc)
34+
* @see org.springframework.context.annotation.ImportSelector#selectImports(org.springframework.core.type.AnnotationMetadata)
35+
*/
36+
@Override
37+
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
38+
39+
List<String> imports = new ArrayList<>();
40+
41+
if (WebStack.WEBMVC.isAvailable()) {
42+
imports.add("org.springframework.hateoas.config.mvc.WebMvcHateoasConfiguration");
43+
}
44+
45+
if (WebStack.WEBFLUX.isAvailable()) {
46+
imports.add("org.springframework.hateoas.config.reactive.WebFluxHateoasConfiguration");
47+
}
48+
49+
return imports.toArray(new String[imports.size()]);
50+
}
51+
}

src/main/java/org/springframework/hateoas/config/mvc/HypermediaWebMvcConfigurer.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import java.util.Arrays;
2424
import java.util.Collection;
25+
import java.util.Collections;
2526
import java.util.List;
2627

2728
import org.springframework.beans.BeansException;
@@ -101,10 +102,10 @@ public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
101102

102103
if (this.hypermediaTypes.contains(HypermediaType.HAL_FORMS)) {
103104

104-
converters.add(0,
105-
new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class,
106-
Collections.singletonList(HAL_FORMS_JSON), createHalFormsObjectMapper(this.mapper, this.curieProvider,
107-
this.relProvider, linkRelationMessageSource, this.halFormsConfiguration)));
105+
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(
106+
ResourceSupport.class, Collections.singletonList(HAL_FORMS_JSON),
107+
createHalFormsObjectMapper(this.mapper, this.curieProvider, this.relProvider, linkRelationMessageSource,
108+
this.halFormsConfiguration)));
108109
}
109110

110111
if (this.hypermediaTypes.contains(HypermediaType.COLLECTION_JSON)) {
@@ -115,8 +116,8 @@ public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
115116

116117
if (this.hypermediaTypes.contains(HypermediaType.UBER)) {
117118

118-
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class,
119-
Collections.singletonList(UBER_JSON), createUberObjectMapper(this.mapper)));
119+
converters.add(0, new TypeConstrainedMappingJackson2HttpMessageConverter(
120+
ResourceSupport.class, Collections.singletonList(UBER_JSON), createUberObjectMapper(this.mapper)));
120121
}
121122
}
122123
}

src/main/java/org/springframework/hateoas/config/mvc/WebMvcHateoasConfiguration.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,21 @@
3030

3131
/**
3232
* Spring MVC HATEOAS Configuration
33-
*
33+
*
3434
* @author Greg Turnquist
3535
*/
3636
@Configuration
3737
public class WebMvcHateoasConfiguration {
3838

3939
@Bean
4040
HypermediaWebMvcConfigurer hypermediaWebMvcConfigurer(ObjectProvider<ObjectMapper> mapper,
41-
DelegatingRelProvider relProvider,
42-
ObjectProvider<CurieProvider> curieProvider,
43-
ObjectProvider<HalConfiguration> halConfiguration,
44-
ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
45-
Collection<HypermediaType> hypermediaTypes) {
46-
47-
return new HypermediaWebMvcConfigurer(
48-
mapper.getIfAvailable(ObjectMapper::new),
49-
relProvider,
50-
curieProvider.getIfAvailable(),
51-
halConfiguration.getIfAvailable(HalConfiguration::new),
52-
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new),
53-
hypermediaTypes);
41+
DelegatingRelProvider relProvider, ObjectProvider<CurieProvider> curieProvider,
42+
ObjectProvider<HalConfiguration> halConfiguration, ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
43+
Collection<HypermediaType> hypermediaTypes) {
44+
45+
return new HypermediaWebMvcConfigurer(mapper.getIfAvailable(ObjectMapper::new), relProvider,
46+
curieProvider.getIfAvailable(), halConfiguration.getIfAvailable(HalConfiguration::new),
47+
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes);
5448
}
5549

5650
@Bean

src/main/java/org/springframework/hateoas/config/reactive/WebFluxHateoasConfiguration.java

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,53 +31,42 @@
3131

3232
/**
3333
* Spring WebFlux HATEOAS configuration.
34-
*
34+
*
3535
* @author Greg Turnquist
3636
* @since 1.0
3737
*/
3838
@Configuration
3939
public class WebFluxHateoasConfiguration {
4040

4141
@Bean
42-
WebClientConfigurer webClientConfigurer(ObjectProvider<ObjectMapper> mapper,
43-
DelegatingRelProvider relProvider,
44-
ObjectProvider<CurieProvider> curieProvider,
45-
ObjectProvider<HalConfiguration> halConfiguration,
46-
ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
47-
Collection<HypermediaType> hypermediaTypes) {
48-
49-
return new WebClientConfigurer(
50-
mapper.getIfAvailable(ObjectMapper::new),
51-
relProvider,
52-
curieProvider.getIfAvailable(),
53-
halConfiguration.getIfAvailable(HalConfiguration::new),
54-
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new),
55-
hypermediaTypes);
42+
WebClientConfigurer webClientConfigurer(ObjectProvider<ObjectMapper> mapper, DelegatingRelProvider relProvider,
43+
ObjectProvider<CurieProvider> curieProvider, ObjectProvider<HalConfiguration> halConfiguration,
44+
ObjectProvider<HalFormsConfiguration> halFormsConfiguration, Collection<HypermediaType> hypermediaTypes) {
45+
46+
return new WebClientConfigurer(mapper.getIfAvailable(ObjectMapper::new), relProvider,
47+
curieProvider.getIfAvailable(), halConfiguration.getIfAvailable(HalConfiguration::new),
48+
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes);
5649
}
5750

5851
@Bean
5952
HypermediaWebClientBeanPostProcessor webClientBeanPostProcessor(WebClientConfigurer configurer) {
6053
return new HypermediaWebClientBeanPostProcessor(configurer);
6154
}
62-
55+
6356
@Bean
6457
HypermediaWebFluxConfigurer hypermediaWebFluxConfigurer(ObjectProvider<ObjectMapper> mapper,
65-
DelegatingRelProvider relProvider,
66-
ObjectProvider<CurieProvider> curieProvider,
67-
ObjectProvider<HalConfiguration> halConfiguration,
68-
ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
69-
Collection<HypermediaType> hypermediaTypes) {
58+
DelegatingRelProvider relProvider, ObjectProvider<CurieProvider> curieProvider,
59+
ObjectProvider<HalConfiguration> halConfiguration, ObjectProvider<HalFormsConfiguration> halFormsConfiguration,
60+
Collection<HypermediaType> hypermediaTypes) {
7061

71-
return new HypermediaWebFluxConfigurer(
72-
mapper.getIfAvailable(ObjectMapper::new),
73-
relProvider,
74-
curieProvider.getIfAvailable(),
75-
halConfiguration.getIfAvailable(HalConfiguration::new),
76-
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes);
62+
return new HypermediaWebFluxConfigurer(mapper.getIfAvailable(ObjectMapper::new), relProvider,
63+
curieProvider.getIfAvailable(), halConfiguration.getIfAvailable(HalConfiguration::new),
64+
halFormsConfiguration.getIfAvailable(HalFormsConfiguration::new), hypermediaTypes);
7765
}
7866

7967
/**
80-
* TODO: Replace with Spring Framework filter when https://github.com/spring-projects/spring-framework/issues/21746 is completed.
68+
* TODO: Replace with Spring Framework filter when https://github.com/spring-projects/spring-framework/issues/21746 is
69+
* completed.
8170
*/
8271
@Bean
8372
HypermediaWebFilter hypermediaWebFilter() {

src/main/java/org/springframework/hateoas/core/DummyInvocationUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)