skip to content

What is MvcUriComponentsBuilder.fromMethodCall / fromMethodName and why is it useful?

level: seniorimportance: should knowfreq 18%

answer

  1. link by controller method, not path string
  2. on(Ctrl.class).method(args) = recording CGLIB proxy
  3. fromMethodCall / fromMethodName / fromMappingName
  4. refactor-safe URLs; basis of HATEOAS linkTo(methodOn)
  5. needs proxyable controller; thread-local host

basics

~10 s

MvcUriComponentsBuilder builds URLs by pointing at a controller method instead of typing its path. fromMethodCall(on(Ctrl.class).getUser(id)) reads the method's @GetMapping and reconstructs the URL, so if you change the mapping the link updates automatically.

solid answer

~40 s

MvcUriComponentsBuilder (org.springframework.web.servlet.mvc.method.annotation) builds URIs from controller mapping metadata rather than hardcoded path strings — type-safe, refactor-friendly links. The main entry points: fromMethodCall(on(BookController.class).getBook(id)) where on() returns a recording proxy that captures the invoked method and arguments; fromMethodName(BookController.class, "getBook", id) which selects the method by name when overloads/proxies are awkward; and fromMappingName("BC#getBook") using the @RequestMapping name. It inspects the method's @RequestMapping/@GetMapping (class + method level), fills path variables from the captured arguments, and returns a UriComponentsBuilder you can further customize. If you rename the URL in the annotation, every generated link follows — no string to forget. It uses the current request for host/context (or relativeTo(...) / withoutMapping variants). The trade-off is reflection/proxy overhead and coupling links to controllers, so it's used more for server-rendered views and HATEOAS-style links than hot paths.

code

java · 19 lines
java
import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.*;
import org.springframework.web.bind.annotation.*;
import java.net.URI;

@RestController
@RequestMapping("/books")
class BookController {
    @GetMapping("/{id}")
    Book getBook(@PathVariable Long id) { /* ... */ return null; }
}

// Elsewhere — build the URL by pointing at the method, not typing the path:
URI self = fromMethodCall(on(BookController.class).getBook(42L))
        .buildAndExpand()
        .toUri();
// -> http://<current-host>/books/42  (path derived from @RequestMapping + @GetMapping)

// By method name (no proxy):
URI same = fromMethodName(BookController.class, "getBook", 42L).build().toUri();

go deeper

for a junior

Know it builds a URL by referencing a controller method instead of hardcoding the path.

for a middle

Know the three entry points (fromMethodCall+on, fromMethodName, fromMappingName) and that it reads @RequestMapping to fill path variables.

for a senior

Explain the recording-proxy mechanism, thread-local/host caveats, relativeTo, and the refactor-safety vs overhead trade-off.

for a principal

Decide where controller-coupled link generation belongs (views/HATEOAS vs services), weigh coupling/overhead, and standardize on HATEOAS linkTo when hypermedia is in play.

## The idea Instead of writing the path of an endpoint as a string (`/books/{id}`), you reference the **controller method** that serves it. Spring reads that method's mapping annotations and rebuilds the URL. Rename the mapping and every generated link updates — the compiler and refactoring tools now help you. ## Class `MvcUriComponentsBuilder` lives in `org.springframework.web.servlet.mvc.method.annotation`. Its static factories return a `UriComponentsBuilder` (so you can keep chaining `queryParam`, `fragment`, etc.). ## Entry points ### 1. `fromMethodCall(Object invocationInfo)` with `on(...)` ```java import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.*; URI uri = fromMethodCall(on(BookController.class).getBook(42)) .buildAndExpand() // path var already captured .toUri(); ``` `on(BookController.class)` returns a **CGLIB proxy** that records which method you call and with which arguments. `fromMethodCall` then reads that recorded invocation. The method actually returns a dummy value; only the invocation metadata matters. ### 2. `fromMethodName(Class, String methodName, Object... args)` ```java URI uri = fromMethodName(BookController.class, "getBook", 42).build().toUri(); ``` Selects the method by name — handy when the proxy approach is inconvenient (e.g., final methods, generics, or you just prefer no proxy). Downside: not compile-checked against renames and ambiguous with overloads. ### 3. `fromMappingName(String name)` Uses the assigned request-mapping name (default convention: capital letters of the controller + `#` + method, e.g. `BC#getBook`, or an explicit `name` on `@RequestMapping`). Mainly used in views (e.g., the Spring JSP `mvcUrl` function). ### 4. Relative / no-context variants `fromMethodCall(...)` uses the current request for scheme/host/context (thread-local, same caveat as ServletUriComponentsBuilder). Overloads and `relativeTo(UriComponentsBuilder base)` let you build against a supplied base instead of the current request — useful off the request thread or for absolute external links. ## What it reads It merges the **class-level** `@RequestMapping` and the **method-level** mapping (`@GetMapping("/{id}")` etc.), resolves `@PathVariable` placeholders from the captured/passed arguments, and can pick up `@RequestParam` values to add query parameters (with `fromMethodCall`). The result is a fully-formed URL for that endpoint. ## Why use it - **Refactor safety**: change the URL in one place (the annotation); all links follow. - **No path-string duplication** scattered across services/views. - Great for **HATEOAS** links (Spring HATEOAS' `WebMvcLinkBuilder.linkTo(methodOn(...))` is the same idea, built on this) and server-side view rendering. ## Trade-offs / gotchas - **Overhead**: proxy creation + reflection per call — negligible for occasional link generation, not for tight loops. - **Thread-local dependency**: the `fromMethodCall`/`fromMethodName` variants that use the current request fail off the request thread; use `relativeTo(...)` there. - **Coupling**: links become coupled to controller signatures; changing a method signature ripples to callers (usually a feature, occasionally friction). - `on(...)` needs a **non-final, proxyable** controller class; final classes/methods break the CGLIB proxy — use `fromMethodName` instead. - Arguments passed to `on(...)` for non-path/query params are ignored for URL building; only mapping-relevant ones matter. ## Relation to Spring HATEOAS If you use Spring HATEOAS, prefer `WebMvcLinkBuilder.linkTo(methodOn(Controller.class).method(args)).withSelfRel()` — it wraps this machinery and returns `Link` objects. Same concept, richer API for hypermedia.

  • What does on(Controller.class) actually return, and why?
    A CGLIB proxy of the controller that records the method you invoke and its arguments (returning a dummy result). fromMethodCall reads that recorded invocation to find the mapping and path-variable values. It requires a non-final, proxyable class.
  • How does this relate to Spring HATEOAS linkTo(methodOn(...))?
    Spring HATEOAS' WebMvcLinkBuilder is built on the same controller-method-introspection mechanism; linkTo(methodOn(Ctrl.class).m(args)) wraps it and returns Link objects for hypermedia responses.

saying these in an interview costs you the question

  • Thinking on(...) actually executes the controller method / hits the DB — it only records the invocation.
  • Believing fromMethodName is compile-time-checked against renames — it is a string and is not.
  • Using it in a tight loop and ignoring proxy/reflection overhead.
  • Expecting on(...) to work on a final controller class or final method.

context