What is the functional endpoint model (WebMvc.fn) in Spring MVC, and what are its core building blocks?
answer
- RouterFunction = routing table
- RequestPredicate -> HandlerFunction
- ServerRequest in, ServerResponse out
- Expose as @Bean, RouterFunctionMapping dispatches
- Spring 5.2, same Servlet stack
basics
~10 sWebMvc.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 sWebMvc.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 linesimport 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
Know the three nouns: RouterFunction, RequestPredicate, HandlerFunction, and that ServerRequest goes in and ServerResponse comes out.
Know it's declared as a @Bean, discovered by RouterFunctionMapping, and that first-match-wins ordering applies.
Articulate that it shares the Servlet stack and message converters with annotated controllers, and the tradeoffs vs annotations.
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)