EDIT:
Updated message to use improved versions - Spring HATEOAS 0.22 and Spring Framework 4.3.5 / 5.0 M4 .
Answer:
It is very interesting. I looked at the source code for the ControllerLinkBuilder linkTo and methodOn , and there is a lot of methodOn for a simple link:
- creates a proxy server for the controller, which records the interactions and receives the method and parameters for creating the link for
- it discovers mappings of this method to build the link
ControllerLinkBuilder very convenient as it avoids duplicating the logic that is already contained in your mapping.
I came up with a simple sample application and a very simple test for measuring and comparing link linker performance
It is based on a simple controller - it simply returns 100 simple objects - each of them carries one link of its own.
@RestController @RequestMapping("items") @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class TestController { private final ItemResourceProcessor resourceProcessor; @RequestMapping(method = GET) public ResponseEntity<List<Resource<Item>>> getAll() { List<Resource<Item>> items = new ArrayList<>(100); for (int i = 0; i < 100; i++) { items.add(resourceProcessor.process( new Resource<>(new Item(i, UUID.randomUUID().toString())))); } return ResponseEntity.ok(items); } @RequestMapping(method = GET, path = "/{id}") public ResponseEntity<Resource<Item>> getOne(@PathVariable Integer id) { return null; } }
ItemResourceProcessor adds simple self-tightening, and I tried and measured three different alternatives:
1. ControllerLinkBuilder with linkTo (methodOn)
Here, ControllerLinkBuilder is used to verify that the controller and the method need aop proxy for each link created.
@Component public class ItemResourceProcessor implements ResourceProcessor<Resource<Item>> { @Override public Resource<Item> process(Resource<Item> resource) { resource.add(linkTo(methodOn(TestController.class).getOne(resource.getContent().getId())).withSelfRel()); return resource; } }
Results for this option:
wrk -t2 -c5 -d30s http://localhost:8080/items Running 30s test @ http://localhost:8080/items 2 threads and 5 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.77ms 0.93ms 25.57ms 83.97% Req/Sec 420.87 48.63 500.00 71.33% 25180 requests in 30.06s, 305.70MB read Requests/sec: 837.63
2. ControllerLinkBuilder without the On () method
The methodOn() call is called methodOn() , and the method reference is determined once when the resource processor is created and reused to generate the link. This version avoids the overhead of the On method, but still detects a method mapping for creating the link.
@Component public class ItemResourceProcessor implements ResourceProcessor<Resource<Item>> { private Method method = ReflectionUtils.findMethod(TestController.class, "getOne", Integer.class); @Override public Resource<Item> process(Resource<Item> resource) { resource.add(linkTo(method, resource.getContent().getId()).withSelfRel()); return resource; } }
The results are slightly better than for the first version. Optimization gives us only small advantages.
wrk -t2 -c5 -d30s http://localhost:8080/items Running 30s test @ http://localhost:8080/items 2 threads and 5 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.02ms 477.64us 13.80ms 84.01% Req/Sec 499.42 18.24 540.00 65.50% 29871 requests in 30.05s, 365.50MB read Requests/sec: 994.03
3. Link generation using BasicLinkBuilder
Here we move away from ControllerLinkBuilder and use BasicLinkBuilder . This implementation does not perform any introspection of controller mappings and is therefore a good candidate for a benchmark.
@Component public class ItemResourceProcessor implements ResourceProcessor<Resource<Item>> { private ControllerLinkBuilder baseLink; @Override public Resource<Item> process(Resource<Item> resource) { resource.add(BasicLinkBuilder.linkToCurrentMapping() .slash("items") .slash(resource.getContent().getId()).withSelfRel()); return resource; } }
The results are better again.
wrk -t2 -c5 -d30s http://localhost:8080/items Running 30s test @ http://localhost:8080/items 2 threads and 5 connections Thread Stats Avg Stdev Max +/- Stdev Latency 3.05ms 683.71us 12.84ms 72.12% Req/Sec 658.31 87.79 828.00 66.67% 39349 requests in 30.03s, 458.91MB read Requests/sec: 1310.14
Summary
Of course, there is an overhead of the On () method. Tests show that 100 links cost us less than 2 ms on average compared to BasicLinkBuilder .
Therefore, when the number of displayed links is not massive, the convenience of ControllerLinkBuilder makes it a good choice for generating links.
DISCLAIMER . I know that my wrk tests are not proper benchmarks, but the results can be repeated and show the same results comparing alternatives - so they can at least give an idea of ββthe size of the differences in performance)