diff --git a/src/main/asciidoc/server.adoc b/src/main/asciidoc/server.adoc index e7bbe40e..56f9a7a7 100644 --- a/src/main/asciidoc/server.adoc +++ b/src/main/asciidoc/server.adoc @@ -267,6 +267,10 @@ curl -v localhost:8080/employees \ [[server.entity-links]] == [[fundamentals.obtaining-links.entity-links]] Using the EntityLinks interface +IMPORTANT: `EntityLinks` and it's various implementations are NOT currently provided out-of-the-box for Spring WebFlux applications. +The contract defined in the `EntityLinks` SPI was originally aimed at Spring Web MVC and doesn't consider Reactor types. +Developing a comparable contract that supports reactive programming is still in progress. + So far, we have created links by pointing to the web-framework implementations (that is, the Spring MVC controllers) and inspected the mapping. In many cases, these classes essentially read and write representations backed by a model class. @@ -285,13 +289,13 @@ Link link = links.linkToItemResource(Customer.class, 1L); `EntityLinks` is available via dependency injection by activating `@EnableHypermediaSupprt` in your Spring MVC configuration. This will cause a variety of default implementations of `EntityLinks` being registered. -The most fundamental one is `ControllerEntityLinks` that inspects SpringMVC and Spring WebFlux controller classes. +The most fundamental one is `ControllerEntityLinks` that inspects SpringMVC controller classes. If you want to register your own implementation of `EntityLinks`, check out <>. [[server.entity-links.controller]] -=== EntityLinks based on Spring MVC and WebFlux controllers +=== EntityLinks based on Spring MVC controllers -Activating entity links functionality causes all the Spring MVC and WebFlux controllers available in the current `ApplicationContext` to be inspected for the `@ExposesResourceFor(…)` annotation. +Activating entity links functionality causes all the Spring MVC controllers available in the current `ApplicationContext` to be inspected for the `@ExposesResourceFor(…)` annotation. The annotation exposes which model type the controller manages. Beyond that, we assume that you adhere to following the URI mapping setup and conventions: @@ -421,10 +425,6 @@ It's registered as primary bean so that it's always the sole injection candidate `ControllerEntityLinks` is the default implementation that will be included in the setup, but users are free to implement and register their own implementations. Making those available to the `EntityLinks` instance available for injection is a matter of registering your implementation as Spring bean. -IMPORTANT: `EntityLinks` and it's various implementations are NOT currently provided out-of-the-box for Spring WebFlux applications. -The contract defined in the `EntityLinks` SPI was originally aimed at Spring Web MVC and doesn't consider Reactor types. -Developing a comparable contract that supports reactive programming is still in progress. - .Declaring a custom EntityLinks implementation ==== [source, java] diff --git a/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java b/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java index 7f91932c..fdf49125 100644 --- a/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java +++ b/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java @@ -28,7 +28,6 @@ import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.springframework.context.support.AbstractMessageSource; import org.springframework.context.support.ReloadableResourceBundleMessageSource; @@ -58,7 +57,6 @@ import org.springframework.util.ClassUtils; * @since 0.19 */ @Configuration -@Import(EntityLinksConfiguration.class) @EnablePluginRegistries({ LinkDiscoverer.class }) public class HateoasConfiguration { diff --git a/src/main/java/org/springframework/hateoas/config/WebMvcEntityLinksConfiguration.java b/src/main/java/org/springframework/hateoas/config/WebMvcEntityLinksConfiguration.java index 15d24d28..9a3387cd 100644 --- a/src/main/java/org/springframework/hateoas/config/WebMvcEntityLinksConfiguration.java +++ b/src/main/java/org/springframework/hateoas/config/WebMvcEntityLinksConfiguration.java @@ -30,7 +30,7 @@ import org.springframework.stereotype.Controller; * @author Oliver Gierke */ @Configuration -class WebMvcEntityLinksConfiguration { +class WebMvcEntityLinksConfiguration extends EntityLinksConfiguration { @Bean ControllerEntityLinksFactoryBean webMvcEntityLinks(ObjectProvider linkBuilderFactory) { diff --git a/src/test/java/org/springframework/hateoas/config/WebStackImportSelectorUnitTest.java b/src/test/java/org/springframework/hateoas/config/WebStackImportSelectorUnitTest.java index df7e65f4..25b6bcf7 100644 --- a/src/test/java/org/springframework/hateoas/config/WebStackImportSelectorUnitTest.java +++ b/src/test/java/org/springframework/hateoas/config/WebStackImportSelectorUnitTest.java @@ -18,9 +18,12 @@ package org.springframework.hateoas.config; import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; +import org.springframework.hateoas.server.EntityLinks; import org.springframework.hateoas.support.WebStack; /** @@ -35,7 +38,7 @@ class WebStackImportSelectorUnitTest { @Test // #973 void activatesAllWebStacksByDefault() { - AnnotationMetadata metadata = new StandardAnnotationMetadata(DefaultHypermedia.class); + AnnotationMetadata metadata = AnnotationMetadata.introspect(DefaultHypermedia.class); assertThat(selector.selectImports(metadata)) .containsExactlyInAnyOrderElementsOf(WebStackImportSelector.CONFIGS.values()); @@ -44,7 +47,7 @@ class WebStackImportSelectorUnitTest { @Test // #973 void activatesWebMvcOnlyifConfigured() { - AnnotationMetadata metadata = new StandardAnnotationMetadata(WebMvcHypermedia.class); + AnnotationMetadata metadata = AnnotationMetadata.introspect(WebMvcHypermedia.class); assertThat(selector.selectImports(metadata)).containsExactly(WebStackImportSelector.CONFIGS.get(WebStack.WEBMVC)); } @@ -52,7 +55,7 @@ class WebStackImportSelectorUnitTest { @Test // #973 void activatesWebFluxOnlyIfConfigured() { - AnnotationMetadata metadata = new StandardAnnotationMetadata(WebFluxHypermedia.class); + AnnotationMetadata metadata = AnnotationMetadata.introspect(WebFluxHypermedia.class); assertThat(selector.selectImports(metadata)).containsExactly(WebStackImportSelector.CONFIGS.get(WebStack.WEBFLUX)); } @@ -60,13 +63,23 @@ class WebStackImportSelectorUnitTest { @Test // #973 void rejectsNoStacksSelected() { - AnnotationMetadata metadata = new StandardAnnotationMetadata(NoStacksHypermedia.class); + AnnotationMetadata metadata = AnnotationMetadata.introspect(NoStacksHypermedia.class); assertThatExceptionOfType(IllegalStateException.class) // .isThrownBy(() -> selector.selectImports(metadata)) // .withMessageContaining(NoStacksHypermedia.class.getName()); } + @Test // #1080 + void noEntityLinksRegisteredForWebFluxBootstrap() throws Exception { + + try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(WebFluxHypermedia.class)) { + + assertThatExceptionOfType(NoSuchBeanDefinitionException.class) // + .isThrownBy(() -> context.getBean(EntityLinks.class)); + } + } + @EnableHypermediaSupport(type = HypermediaType.HAL) static class DefaultHypermedia {}