Spring BootSpring Mvcintermediate
Updated:

Spring Boot Spring MVC Interview Questions and Answers

8 min read

Spring MVC interview questions for Spring Boot roles — DispatcherServlet flow, controller annotations, request binding, validation, ResponseEntity and interceptors vs filters.

TL;DR – Quick Answer

Spring MVC is the web layer inside Spring Boot: a front controller (DispatcherServlet) routes each request through a handler mapping to a controller method, binds parameters, runs the handler, and writes the response. Interviews test the request flow, the controller and mapping annotations, how @PathVariable, @RequestParam and @RequestBody differ, validation with @Valid, ResponseEntity, and interceptors versus filters.

On This Page

Spring MVC is the web layer nearly every Spring Boot service exposes, so it turns up in almost every Spring Boot interview. The questions cluster around one thing: do you understand how a request travels from the socket to your controller method and back, and do you know which annotation binds which part of that request. Answering with the DispatcherServlet flow and the right binding choices is what separates someone who copied a controller from someone who understands the framework.

Walk me through what happens when a request hits a Spring MVC application.

The DispatcherServlet (the front controller) receives the request, asks a HandlerMapping which controller method handles that URL and verb, invokes it through a HandlerAdapter, and takes the return value. For an API it serializes the returned object to the response body via a message converter; for a server-rendered app it passes a view name to a ViewResolver which renders the view.

Everything routes through that single servlet — that's the front-controller pattern, and it's why cross-cutting concerns can be applied in one place. The handler mapping matches on path, method, headers and content type; the handler adapter knows how to call an @RequestMapping method, binding its arguments along the way. Understanding this pipeline is the backbone of every other MVC answer, because each annotation plugs into one stage of it.

Interview note: Follow-up: "who decides whether the response is JSON or a rendered page?" For @ResponseBody/@RestController methods, HttpMessageConverters serialize the return value using content negotiation. For view-returning methods, the ViewResolver picks the template. The presence of @ResponseBody is the fork in the road.

What is the difference between @Controller and @RestController?

@RestController is a shortcut for @Controller + @ResponseBody. A @Controller method returns a view name by default; a @RestController method returns data that is serialized straight into the response body. Use @RestController for REST APIs and @Controller when you render HTML views.

The mechanics matter: without @ResponseBody, Spring treats the returned String as a view name and hands it to the ViewResolver, which is why a REST method on a plain @Controller that returns "ok" tries to render a template called ok and 404s the view. @RestController removes that footgun for API classes by applying @ResponseBody to every method.

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public UserDto get(@PathVariable Long id) {   // serialized to JSON automatically
        return userService.findById(id);
    }
}

Interview note: Trap: "you put a @RestController method's return type as String and it returned the literal text, not JSON — is that a bug?" No — a raw String is written by the string message converter as text/plain. To get a JSON string you return an object or a Map, or explicitly produce application/json. Knowing why reveals you understand message converters.

How do the request-mapping annotations work?

@RequestMapping maps a request to a handler by path, HTTP method, params, headers and media type. The composed shortcuts — @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping — are @RequestMapping pre-set to a verb, and are the idiomatic choice.

Class-level @RequestMapping sets a base path that method-level mappings extend, so you keep the resource prefix in one place. Beyond the verb and path you can narrow a mapping by produces and consumes (content negotiation) and by params/headers, which lets two methods share a URL but differ on what they accept or return.

@PostMapping(path = "/api/orders", consumes = "application/json",
             produces = "application/json")
public ResponseEntity<OrderDto> create(@Valid @RequestBody OrderRequest req) {
    OrderDto created = orderService.create(req);
    return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

Interview note: Follow-up: "why prefer @GetMapping over @RequestMapping(method = GET)?" It's more readable and less error-prone — the verb is part of the annotation name, so you can't forget to set method. They compile to the same thing; the shortcut is the convention.

@PathVariable vs @RequestParam vs @RequestBody — how do you choose?

@PathVariable binds a value that is part of the URL path (/users/42id = 42) and identifies a resource. @RequestParam binds a query-string or form parameter (?status=active) for filters and options. @RequestBody deserializes the entire request body — usually JSON — into an object, for create/update payloads.

The rule of thumb: path variables identify what, request params refine how, request bodies carry the data. Mixing them up is a common design smell — putting a filter in the path (/users/status/active) instead of a query param, or trying to read a large payload from query params. @RequestParam supports required and defaultValue; @PathVariable values are mandatory by nature since they're part of the route.

@GetMapping("/api/users/{id}/orders")
public List<OrderDto> orders(
        @PathVariable Long id,                                   // which user
        @RequestParam(defaultValue = "ALL") String status,      // filter
        @RequestParam(defaultValue = "0") int page) {           // option
    return orderService.forUser(id, status, page);
}

Interview note: Trap: "can you have both @RequestBody and multiple @RequestParam on one method?" Yes, but only one @RequestBody per method — the body is a single stream that can be read once. Multiple @RequestBody parameters is a design error, not just a limitation.

How does content negotiation work?

Spring picks the response format by matching the request's Accept header (or a path/param strategy) against the media types each HttpMessageConverter can produce. If the client sends Accept: application/json and Jackson is on the classpath, the object is serialized as JSON; the produces attribute on the mapping constrains what a handler will emit.

In a Spring Boot API this is why returning a POJO "just becomes JSON" — Jackson's converter is auto-configured and JSON is the negotiated default. Add an XML converter and the same endpoint can serve XML when the client asks for it. The concept the interviewer wants: the controller returns an object, and a converter chosen by content negotiation decides the wire format — the controller doesn't hard-code JSON.

Interview note: Follow-up: "how do you force an endpoint to only ever return JSON?" Set produces = MediaType.APPLICATION_JSON_VALUE on the mapping. A request with an incompatible Accept header then gets 406 Not Acceptable instead of a surprise format.

How do you validate request data, and what happens on failure?

Put jakarta.validation constraints (@NotNull, @NotBlank, @Size, @Email, @Min) on the object's fields and @Valid on the controller parameter. On a violation Spring throws MethodArgumentNotValidException (for @RequestBody) before your method body runs, which you translate to a 400 with @ControllerAdvice.

The Jakarta namespace is the current detail — in Spring Boot 3 / Spring 6 the imports are jakarta.validation.*, not javax.validation.*. Validation short-circuits the handler, so your business logic never sees invalid input, and centralizing the error handling keeps every endpoint's 400 response consistent.

public record OrderRequest(
        @NotBlank String sku,
        @Min(1) int quantity,
        @Email String customerEmail) { }

@PostMapping("/api/orders")
public OrderDto create(@Valid @RequestBody OrderRequest req) {
    return orderService.create(req);
}

Interview note: Trap: "you added @Size but validation never runs — why?" Usually a missing @Valid on the parameter, or the validation starter isn't on the classpath (spring-boot-starter-validation). Constraints without @Valid, or without the validator on the classpath, are silently ignored — a frequent "why isn't it validating" bug.

What is ResponseEntity and when do you use it?

ResponseEntity<T> is a wrapper that lets you control the full HTTP response — status code, headers and body — instead of relying on the default 200. You use it when the status varies (201 Created, 404 Not Found, 204 No Content) or when you need to set headers like Location.

Returning a bare object always yields 200 (unless you add @ResponseStatus), which is fine for simple reads but wrong for creates and conditional responses. ResponseEntity makes the status a first-class part of the return, which is why REST-correct APIs use it for anything beyond a plain fetch.

@GetMapping("/api/users/{id}")
public ResponseEntity<UserDto> get(@PathVariable Long id) {
    return userService.find(id)
        .map(ResponseEntity::ok)                       // 200 with body
        .orElseGet(() -> ResponseEntity.notFound().build());  // 404, no body
}

Interview note: Follow-up: "ResponseEntity vs @ResponseStatus — when each?" @ResponseStatus sets a fixed status for a method or exception type; ResponseEntity sets it dynamically per call. Fixed outcome → annotation; outcome depends on the result → ResponseEntity.

Interceptors vs filters — what's the difference and when do you use each?

A servlet Filter runs at the servlet-container level, wrapping the request before and after Spring MVC processes it — ideal for concerns that don't need MVC context, like logging, compression or CORS. A HandlerInterceptor is Spring-MVC-aware and runs around handler execution, with preHandle, postHandle and afterCompletion hooks that can see the resolved handler, the model and any exception.

Choose by what you need access to. If you only touch raw request/response, a filter is simpler and runs earlier. If you need the matched handler, the model, or to short-circuit based on controller-level metadata (say, a custom auth annotation on the handler method), an interceptor is the right layer because it executes inside Spring MVC where that context exists.

@Component
public class AuditInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
        MDC.put("route", req.getRequestURI());
        return true;   // false short-circuits the request before the controller
    }
    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse res,
                                Object handler, Exception ex) {
        MDC.clear();
    }
}

Interview note: Trap: "which runs first, filter or interceptor?" The filter — it's outside the DispatcherServlet, so it wraps the entire dispatch, including the interceptor chain. Order: filter → DispatcherServlet → interceptor.preHandle → controller → interceptor.postHandle → interceptor.afterCompletion → filter.

How do you handle exceptions thrown by controllers?

Centralize them with @ControllerAdvice (or @RestControllerAdvice) plus @ExceptionHandler methods. A single advice class catches exceptions from every controller and maps each type to a clean HTTP response, so error formatting lives in one place instead of being duplicated in try/catch blocks.

This keeps controllers focused on the happy path. A @RestControllerAdvice with an @ExceptionHandler(MethodArgumentNotValidException.class) turns validation failures into a consistent 400 body across the whole API, and a handler for your domain's NotFoundException maps it to 404. It's the standard Spring MVC answer to "where does error handling go."

@RestControllerAdvice
public class ApiExceptionHandler {
    @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<ApiError> notFound(NoSuchElementException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body(new ApiError("NOT_FOUND", e.getMessage()));
    }
}

Interview note: Follow-up: "how do you keep this consistent across many services?" Extend ResponseEntityExceptionHandler for the framework exceptions and share a base advice, or standardize on RFC 7807 ProblemDetail, which Spring 6 supports natively for a uniform error shape.

What interviewers really test

Spring MVC questions check that you can trace a request end-to-end and pick the right tool at each stage: the correct binding annotation, the right validation wiring, ResponseEntity where status matters, and interceptors versus filters chosen by the context they need. Vague answers ("the controller handles it") don't survive the first follow-up about the DispatcherServlet or message converters.

Build the mechanics through the Spring Boot learning path, then round out the web layer with the two topics MVC always drags in: exception handling, since real controllers must fail cleanly, and Spring Data JPA, since most controllers ultimately read and write a database. When you can narrate a request from DispatcherServlet to JSON response and back, a Spring MVC mock interview stops being a memory test and becomes a walk through code you understand.

Frequently Asked Questions

What is the DispatcherServlet in Spring MVC?
It is the front controller — a single servlet that receives every request, consults a HandlerMapping to find the right controller method, invokes it via a HandlerAdapter, and renders the result through a ViewResolver or writes the body directly. Spring Boot auto-configures it, so you rarely declare it yourself.
What's the difference between @Controller and @RestController?
@Controller returns view names by default and needs @ResponseBody on methods that return data. @RestController is @Controller plus @ResponseBody applied to every method, so it returns serialized objects (usually JSON) directly. Use @RestController for REST APIs and @Controller for server-rendered views.
When do I use @PathVariable versus @RequestParam?
@PathVariable binds a value embedded in the URL path (/users/42), used for identifying a resource. @RequestParam binds a query-string or form parameter (?status=active), used for filters and options. @RequestBody binds the whole request body, typically JSON, into an object.
How do I validate incoming request data in Spring MVC?
Annotate the target object's fields with jakarta.validation constraints like @NotNull and @Size, and put @Valid on the controller parameter. A validation failure throws MethodArgumentNotValidException, which you handle centrally with @ControllerAdvice to return a clean 400 response.
What's the difference between a HandlerInterceptor and a servlet Filter?
A Filter is a servlet-level component that wraps every request before Spring MVC sees it, good for cross-cutting concerns like logging and CORS. A HandlerInterceptor is Spring-MVC-aware and runs around handler execution, so it has access to the resolved handler and can act before, after and on completion of the controller method.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Java Full Stack program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 16 July 2026 LinkedIn
Chat with us