skip to content

What is the functional endpoint model (WebMvc.fn) in Spring MVC, and what are its core building blocks?

level: juniorimportance: should knowfreq 35%

answer

  1. RouterFunction = routing table
  2. RequestPredicate -> HandlerFunction
  3. ServerRequest in, ServerResponse out
  4. Expose as @Bean, RouterFunctionMapping dispatches
  5. Spring 5.2, same Servlet stack

basics

~10 s

WebMvc.fn is an alternative to @Controller where you define routes in code. A RouterFunction maps a RequestPredicate (like GET /hello) to a HandlerFunction that takes a ServerRequest and returns a ServerResponse.

solid answer

~40 s

WebMvc.fn is Spring MVC's functional programming model (added in Spring 5.2), an alternative to annotation-based @Controller. Instead of annotations, you declare routing explicitly with a RouterFunction<ServerResponse>, built via the RouterFunctions.route() builder. Each route pairs a RequestPredicate (the matching condition, e.g. GET("/users/{id}")) with a HandlerFunction<ServerResponse> — a lambda that receives a ServerRequest and returns a ServerResponse. You expose the RouterFunction as a @Bean; Spring's RouterFunctionMapping discovers it and dispatches matching requests. Routing is data, not metadata: it's expressed as ordinary code you can compose, nest, and test. It still runs on the same blocking Servlet stack and uses the same HttpMessageConverters as annotated controllers.

code

java · 16 lines
java
import static org.springframework.web.servlet.function.RequestPredicates.accept;
import org.springframework.web.servlet.function.*;
import org.springframework.http.MediaType;
import org.springframework.context.annotation.*;

@Configuration
public class RoutingConfig {

    @Bean
    RouterFunction<ServerResponse> greetingRoutes() {
        return RouterFunctions.route()
            .GET("/hello", accept(MediaType.TEXT_PLAIN),
                 request -> ServerResponse.ok().body("Hello, WebMvc.fn"))
            .build();
    }
}

go deeper

for a junior

Know the three nouns: RouterFunction, RequestPredicate, HandlerFunction, and that ServerRequest goes in and ServerResponse comes out.

for a middle

Know it's declared as a @Bean, discovered by RouterFunctionMapping, and that first-match-wins ordering applies.

for a senior

Articulate that it shares the Servlet stack and message converters with annotated controllers, and the tradeoffs vs annotations.

for a principal

Frame it as routing-as-code enabling composition/generation, and reason about when centralized functional routing improves a codebase over scattered annotations.

## What it is **WebMvc.fn** is the *functional* web programming model for Spring MVC (the blocking, Servlet-based stack), introduced in **Spring Framework 5.2**. It is a lightweight alternative to the annotation model (`@RestController` / `@RequestMapping` / `@GetMapping`). The reactive stack (Spring WebFlux) has an analogous model called **WebFlux.fn**; the two share almost identical APIs but live in different packages (`org.springframework.web.servlet.function` vs `org.springframework.web.reactive.function.server`). ## The building blocks - **`RouterFunction<ServerResponse>`** — the routing table. Given a `ServerRequest`, it returns an `Optional<HandlerFunction<ServerResponse>>`: the handler to run, or empty if this router doesn't match. You almost never implement it by hand; you build it with the fluent `RouterFunctions.route()` builder. - **`RequestPredicate`** — the *condition* for a route (HTTP method, path pattern, content type, accept header, params, headers). Factory methods live in `RequestPredicates` (e.g. `GET`, `POST`, `path`, `accept`, `contentType`, `queryParam`). Predicates compose with `.and(...)` and `.or(...)`. - **`HandlerFunction<ServerResponse>`** — a functional interface: `ServerResponse handle(ServerRequest request) throws Exception`. This is your endpoint logic. Because it can throw `Exception`, handlers can propagate errors normally. - **`ServerRequest`** — an immutable view over the incoming request: path variables (`pathVariable`), query params (`param`), headers, and the body (`body(Class)`), read via the same `HttpMessageConverter`s as annotated controllers. - **`ServerResponse`** — a builder-style, immutable response: `ServerResponse.ok().body(obj)`, `ServerResponse.status(...)`, `ServerResponse.created(uri).build()`, etc. The body is serialized by message converters. ## How a request flows 1. You declare a `@Bean` that returns a `RouterFunction<ServerResponse>`. 2. Spring's **`RouterFunctionMapping`** collects all such beans at startup and combines them. 3. On each request, the `DispatcherServlet` consults handler mappings; `RouterFunctionMapping` asks each router whether it matches. The **first** matching route wins (order matters — routes are evaluated top to bottom). 4. The matched `HandlerFunction` runs and returns a `ServerResponse`, which is written to the client. ## Minimal example ```java @Bean RouterFunction<ServerResponse> routes(GreetingHandler h) { return RouterFunctions.route() .GET("/hello", accept(MediaType.TEXT_PLAIN), h::hello) .build(); } ``` ## When to use it - You want routing to be **explicit and centralized** rather than scattered across annotated classes. - You want to **compose or generate** routes programmatically (e.g. build many similar endpoints in a loop). - You prefer immutable request/response objects and lambda handlers. ## Gotchas - It runs on the **same blocking Servlet stack** — it is NOT reactive and does not make anything non-blocking. - **Order matters**: the first matching predicate wins, so put specific routes before general ones. - You lose some annotation conveniences (e.g. `@Valid` auto-triggering, `@ExceptionHandler` semantics differ) — validation and error handling become explicit. - Functional and annotated endpoints **coexist** in the same app; you don't have to choose one exclusively.

  • Does using WebMvc.fn make my endpoints non-blocking or reactive?
    No. WebMvc.fn runs on the same blocking Servlet stack as annotated controllers. It only changes how routing is declared. The reactive equivalent is WebFlux.fn on the WebFlux stack.
  • How does Spring find your RouterFunction?
    You expose it as a @Bean returning RouterFunction<ServerResponse>. At startup RouterFunctionMapping collects all such beans and combines them; DispatcherServlet dispatches matching requests to them.

saying these in an interview costs you the question

  • Claiming WebMvc.fn is reactive or non-blocking
  • Thinking functional endpoints replace and cannot coexist with @Controller
  • Confusing WebMvc.fn (servlet, org.springframework.web.servlet.function) with WebFlux.fn (reactive)

context