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
We now use the API introduced in Spring Framework 5.2.2 to customize a WebClient to add hypermedia related en- and decoders without dropping customizations potentially made to the WebClient instance.
RestTemplateHateoasConfiguration depends on a shared bean WebConverters (previously WebMvcConverters). As it can safely be used in both WebFlux and WebMVC scenarios (as it only depends on types from spring-web), we now include a declaration in the shared HateoasConfiguration. This also allows us to remove the just introduced explicit declaration of a WebConverter bean in WebFluxHateoasConfiguration.
UriTemplate now uses UriBuilderFactory (DefaultUriBuilderFactory in particular) to expand templates. We inspect the given source URI string, try to decode it and configure the factory to only encode values if the decoded String is shorter than the source one as that indicates it already contains encoded characters.
The UriBuilderFactory is held as transient value as its implementations are usually not serializable in the first place. Added the necessary logic to recreate the factory instance on deserialization.
Added all expansion tests given in the original ticket as unit tests.
RestTemplateHateoasConfiguration previously referred to HypermediaWebMvcConfigurer which is turn depending on WebMvcConfigurer, a type living in spring-webmvc. The very former would still get active in WebFlux as the web stack is selected based on the presence of either DispatcherServlet or DispatcherHandler. This is now resolved by using a WebMvcConverter indrection to handle the configuration tweaks avoiding the reference to interfaces from spring-webmvc.
Introduced an ArchUnit based test that Spring HATEOAS code only depends on Spring types containing references to reactive types from either the ….reactive package or classes starting with WebFlux.
We constrained the RepresentationModel type parameter in a way that non of the RepresentationModel subclasses we provide aren't usable with it. That's now fixed by loosing that restriction to an arbitrary type.
We now use Spring's ConcurrentReferenceHashMap to keep instances of UriTemplate cached but at the same time allow the cache entries to be garbage collected if necessary.
org.springframework.hateoas.MediaTypes.HAL_JSON_UTF8 value was removed and only HAL_JSON is present. UTF8 is considered as default encoding for application/hal+json type.
Backported to 1.0.x.
We now explicitly equip the resource bundle base name with a "classpath:" prefix so that the resource lookup also works in Boot web applications in which the ResourceLoader setup is tweaked to prefer ServletContext resources.