This commit adds String constants to IanaLinkRelations, so that IanaLinkRelations can be leveraged in places where compile time constants are required, such as Relation#itemRelation and Relation#collectionRelation.
Original issue: #1216
Switched to a less complicated implementation of the interface method parameter annotation lookup by using Spring's ClassUtils.getInterfaceMethodIfPossible(…).
Simplified test cases to pure unit test on the link builder APIs. We don't need to fully execute a complete MVC/WebFlux request/response cycle to verify the link creation to pick up the parameter annotations from the interfaces.
Original pull request: #1194.
When forming links, look at a controller class's interface definitions for possible Spring Web annotations.
Related issues: spring-projects/spring-framework#15682
Original pull request: #1194.
Backports of:
#1174 - Upgrade to Spring Framework 5.2.3.
#1178 - Upgrade to Jackson 2.10.2.
#1179 - Upgrade to Kotlin 1.3.61.
#1180 - Upgrade to Slf4J 1.7.30.
#1181 - Upgrade to Reactor Dysprosium SR3.
#1182 - Upgrade to JUnit 5.5.2.
RestTemplateHateoasConfiguration is a BeanPostProcessor and previously used a direct reference to WebConverters which caused downstream dependencies to be initialized once Spring Framework looks up BeanPostProcessors. This renders all of those dependencies ineligible of being post-processed by other BeanPostProcessors in turn.
This is now fixed by using an ObjectProvider<WebConverters> from within RestTemplateHateoasConfiguration instead, so that the initialization is delayed until the processing of the first bean is triggered.
Backport of: #1166.
Using a custom serializer seems to break downstream projects that also register serializers for EntityModel. We're now using extra methods on EntityModel itself that do the trick as well.
We now keep internal methods in EntityModel that are configured using Jackson annotations so that it'd populate the content with a Map in case Map<String, Content> is defined as payload type.
We now register a custom serializer to massage EntityModel instances into dedicated types that – in case of a Map being the content of the entity model – wrap the model into a type that applies the necessary Jackson tweaks to properly unwrap a Map.
Jackson can be configured to not auto-detect properties on objects to be rendered, requiring them to be explicitly annotated with @JsonProperty to be exposed. In such a configuration setup, some of our model types do not work properly as so far we have expected public properties to be included automatically.
This commit changes that to explicitly include @JsonProperty on all of the getter methods exposed.
The base URI we create now consists of the requests root URI plus the context path concatenated. On actual UriComponentsBuilder creation we then simply append the path discovered from the method mapping.
The implementation details of WebHandler have been significantly refactored to rather work with structures that allow better cacheability by clearly separating abstractions over the statically available information from the per-invocation aspects. This results in a new HandlerMethodParameter(s) abstraction within WebHandler. BoundMethodParameter has been removed entirely. HandlerMethodParameters are create once then cached for every controller method being linked to.
DummyInvocationUtils now creates a ThreadLocal cache of the proxies created for calls to methodOn(…) as they essentially only act as basis for subsequent calls to the methods on the proxy created which in turn are expected to be handed into a linkTo(…) call which obtains the invocation right away. This avoids overhead in cases methodOn(…) is called multiple times for the same controller from a single controller.
The lookup of the LastInvocationAware was previously routed through the proxy, handled by InvocationRecordingMethodInterceptor. This resulted in a second, reflective call for every link creation. DummyInvocationUtils now provides a dedicated lookup method as it knows about the structure of the proxy it created and thus can unfold the recorded invocation more effectively.
The LinkBuilder type hierarchy now works with UriComponents and only creates a UriComponentsBuilder if it needs to modify the backing link in the first place. This avoids superfluous back and forth between UriComponents and UriComponentsBuilders that involved quite a bit of String parsing and creation.
EncodingUtils now starts from a StandardCharsets.UTF_8 to avoid repeated Charset creation.
The changes result in a ~3x performance compared to 1.0.2.RELEASE:
1.0.2.RELEASE
Benchmark Mode Cnt Score Error Units
ControllerLinkBuilderBenchmark.noLinkCreation thrpt 10 39004583,189 ± 751668,181 ops/s
ControllerLinkBuilderBenchmark.pureLinkCreation thrpt 10 43443,133 ± 783,120 ops/s
ControllerLinkBuilderBenchmark.withLinkCreation thrpt 10 60201,629 ± 1292,179 ops/s
1.1 / 1.0.3 SNAPSHOT
Benchmark Mode Cnt Score Error Units
ControllerLinkBuilderBenchmark.noLinkCreation thrpt 10 39618560,950 ± 612794,310 ops/s
ControllerLinkBuilderBenchmark.pureLinkCreation thrpt 10 121700,634 ± 1510,415 ops/s
ControllerLinkBuilderBenchmark.withLinkCreation thrpt 10 121982,085 ± 3344,206 ops/s
noLinkCreation - creates a single RepresentationModel instance but adds no links
pureLinkCreation - creates a single link pointing to a controller method
withLinkCreation - creates a single RepresentationModel instance adding a single link