All articles
Spring MVCExam Tips

Spring MVC & REST Exam Guide (2V0-72.22)

May 21, 202617 min read

What Is Spring MVC

Spring MVC is the servlet-based web framework that ships inside Spring Framework. It is built on the front-controller pattern: every HTTP request that reaches the application passes through a single dispatcher servlet, which then routes the request to the right handler method, lets that method produce a result, and turns the result into an HTTP response. The "MVC" in the name is the classic Model-View-Controller separation — controllers handle requests, the model carries data, and views render it — but on a modern Spring service the "view" is increasingly just JSON written straight to the response body.

That shift matters for the exam. On the 2V0-72.22 exam, Spring MVC carries approximately 10% of all questions — roughly 6 out of 60. But those questions are overwhelmingly about building REST endpoints: @RestController, request mapping, binding arguments, producing responses, content negotiation, and exception handling. Server-side view rendering (Thymeleaf, JSP, ViewResolver) is barely tested. If you spend your study time learning how to render an HTML template, you are optimising for the wrong half of the topic.

This guide targets Spring Framework 5.3 / Spring Boot 2.x — the javax.servlet namespace, with RestTemplate as the synchronous REST client. That is exactly the stack the 2V0-72.22 exam is written against, so the APIs and annotations here are the ones the questions expect.

Exam tip: The exam emphasises REST endpoints far more than view rendering. Budget your study time accordingly — @RestController, @RequestMapping, ResponseEntity, content negotiation, and @RestControllerAdvice are where the points are.

If you are still mapping out your preparation, the 8-week study plan slots Spring MVC alongside the web and REST sections and tells you how much time each topic deserves.


The DispatcherServlet Request Lifecycle

The DispatcherServlet is the front controller — the single entry point for every request handled by Spring MVC. Nothing in your application talks to the raw servlet API directly; instead the dispatcher receives the request and orchestrates a fixed sequence of collaborators to produce a response.

The flow is always the same. The DispatcherServlet first consults its HandlerMapping beans to find which handler (controller method) should service the request, based on the URL, HTTP method, and other conditions. It then asks a HandlerAdapter to actually invoke that handler — the adapter knows how to call the method, resolve its arguments, and capture its return value. What happens next depends on the kind of endpoint. For a REST controller the return value is handed to an HttpMessageConverter, which serialises the object (typically to JSON) and writes it to the response body. For a traditional MVC view controller the handler returns a logical view name, and the dispatcher runs a ViewResolver to locate a View, which then renders the model into HTML.

DispatcherServletthe front-controller request lifecycle (REST path)
HTTP REQUEST
GET /api/books/42 · Accept: application/json
routes to
DispatcherServlet
front controller — the single entry point for every request
asks
HandlerMapping
finds the @RequestMapping handler method for the URL + verb
calls via
HandlerAdapter
invokes the controller method · resolves & binds arguments
returns to
@RestController method
runs business logic · returns a Java object (Book)
writes via
HttpMessageConverter
Jackson serialises the object → JSON (the @ResponseBody path)
then
HTTP RESPONSE
200 OK · Content-Type: application/json
  • DispatcherServlet is the single front controller — it delegates, it does not contain business logic.
  • For a @Controller returning a view name, the last step is ViewResolver → View instead of an HttpMessageConverter.
  • Message converters (e.g. Jackson) only run on the @ResponseBody / @RestController path.
  • No HttpMessageConverter on the classpath (e.g. no Jackson) means no JSON — a common cause of 406 / empty bodies.

In a plain Spring application you registered the DispatcherServlet yourself in web.xml (or via a WebApplicationInitializer). With Spring Boot none of that is needed: the spring-boot-starter-web auto-configuration registers the DispatcherServlet automatically and maps it to /, so it handles every incoming path out of the box. There is no web.xml in a Boot application.

Exam tip: Know the ordered collaborators by heart: HandlerMapping (find the handler) → HandlerAdapter (invoke it) → the handler method runs → then either an HttpMessageConverter (REST, writes the body) or a ViewResolver + View (MVC, renders HTML). The exam frequently asks which component does which step.


@Controller vs @RestController

The single most important distinction in the whole topic is what a controller does with its return value, and that is decided by which stereotype annotation you put on the class.

@Controller
public class PageController {
    @GetMapping("/home")
    public String home() {
        return "home";          // resolved to a VIEW named "home"
    }
}
 
@RestController                  // = @Controller + @ResponseBody
public class BookApiController {
    @GetMapping("/api/books/{id}")
    public Book getBook(@PathVariable Long id) {
        return bookService.find(id);   // serialised to the RESPONSE BODY (JSON)
    }
}

With a plain @Controller, the String returned from a method is interpreted as a logical view name: Spring hands "home" to a ViewResolver, which finds a template and renders it. The return value is not the response body.

@RestController is a composed annotation — it is literally @Controller plus @ResponseBody. The @ResponseBody part changes the meaning of the return value: instead of being a view name, the returned object is passed to an HttpMessageConverter and written directly into the response body. So getBook returns a Book, Jackson serialises it to JSON, and that JSON is the HTTP response. You do not have to annotate each method with @ResponseBody because @RestController applies it to every handler in the class.

Exam tip: A classic trap — a @Controller method that returns "book" intending to send JSON actually resolves a view called book (and usually fails with a missing-view error). To return data as the body you need @RestController or a method-level @ResponseBody.


Request Mapping

@RequestMapping is how you bind a URL (and the conditions around it) to a handler method. It can sit on the class to define a base path, and on each method to refine it.

@RestController
@RequestMapping("/api/books")          // class-level base path
public class BookApiController {
 
    @GetMapping("/{id}")                // GET /api/books/{id}
    public Book get(@PathVariable Long id) { ... }
 
    @GetMapping(params = "author")      // GET /api/books?author=...
    public List<Book> byAuthor(@RequestParam String author) { ... }
 
    @PostMapping(consumes = "application/json", produces = "application/json")
    public Book create(@RequestBody Book book) { ... }
 
    @PutMapping("/{id}")
    public Book update(@PathVariable Long id, @RequestBody Book book) { ... }
 
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) { ... }
}

@RequestMapping is the general-purpose annotation: it can match any HTTP method (or all of them) and accepts a full set of narrowing attributes. Since Spring 4.3 there are method-specific shortcuts@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping — each of which is just @RequestMapping pre-configured for one HTTP method. They are the idiomatic choice and the form the exam expects in well-written code.

Every mapping annotation can be narrowed by several attributes, so two methods can share a path and still be told apart by, say, which query parameter is present or what content type the client sent:

AttributeNarrows byExample
path / valueThe URL path@GetMapping("/{id}")
methodThe HTTP method@RequestMapping(method = RequestMethod.GET)
paramsPresence/value of a query parameter@GetMapping(params = "author")
headersPresence/value of a request header@GetMapping(headers = "X-API-Version=2")
consumesThe request Content-Typeconsumes = "application/json"
producesThe acceptable response type (Accept)produces = "application/json"

The class-level and method-level paths concatenate: with @RequestMapping("/api/books") on the class and @GetMapping("/{id}") on the method, the effective mapping is GET /api/books/{id}.

Exam tip: @GetMapping is exactly equivalent to @RequestMapping(method = RequestMethod.GET) — same for the other verbs. And remember that class-level + method-level paths concatenate; the exam likes to show both and ask for the final URL.


Handler Method Arguments

Spring resolves the parameters of a handler method for you, pulling each value from a different part of the request based on the annotation you use.

@GetMapping("/api/books/{id}")
public Book get(
        @PathVariable Long id,                       // from the URI path
        @RequestParam(defaultValue = "false") boolean full,  // from the query string
        @RequestHeader("Accept-Language") String lang) {     // from a header
    ...
}
 
@PostMapping("/api/books")
public Book create(@RequestBody Book book) {          // from the request body (JSON)
    ...
}

The three you must know cold are @PathVariable, @RequestParam, and @RequestBody:

AnnotationBinds fromNotes
@PathVariableA URI template variable, e.g. /books/{id}The name must match the {placeholder} (or be given explicitly). Required by default.
@RequestParamA query-string or form parameter, e.g. ?id=42Supports required (default true) and defaultValue (which makes it effectively optional).
@RequestBodyThe deserialised request body (e.g. JSON)An HttpMessageConverter turns the body into the parameter type. Typically one per method.

By default @RequestParam is required, so a missing parameter yields a 400 Bad Request; setting defaultValue (or required = false) relaxes that. @RequestHeader works the same way for headers, and @PathVariable is required because the URL would not match the mapping at all without the segment.

Exam tip: The most-tested confusion is @PathVariable vs @RequestParam. Remember the shape of the URL: /{id} in the path ⇒ @PathVariable; ?id= in the query string ⇒ @RequestParam.


Producing Responses

There are three ways a handler controls the HTTP response, and the exam expects you to pick the right one for the situation.

// 1. Plain object → 200 OK + serialised body
@GetMapping("/api/books/{id}")
public Book get(@PathVariable Long id) { return service.find(id); }
 
// 2. ResponseEntity → full control of status, headers and body
@PostMapping("/api/books")
public ResponseEntity<Book> create(@RequestBody Book book) {
    Book saved = service.save(book);
    return ResponseEntity
            .status(HttpStatus.CREATED)               // 201
            .header("Location", "/api/books/" + saved.getId())
            .body(saved);
}
 
// 3. @ResponseStatus → fixed status for a void / object method
@ResponseStatus(HttpStatus.NO_CONTENT)                // 204
@DeleteMapping("/api/books/{id}")
public void delete(@PathVariable Long id) { service.delete(id); }

Returning a plain object is the simplest case: the object is serialised to the body and the response is always 200 OK. ResponseEntity<T> wraps the body together with a status code and headers, and you build it at runtime — use it when the status or headers vary per request (a 201 Created with a Location header on success, a 404 when nothing is found). @ResponseStatus declares a fixed status on the method (or on an exception class); it is ideal for a method that always returns the same code, such as a delete that returns 204 No Content.

The status codes the exam draws on most:

CodeMeaningTypical trigger
200 OKSuccess with a bodyDefault for a returned object
201 CreatedResource createdPOST that creates an entity
204 No ContentSuccess, no bodyDELETE, or an update that returns nothing
400 Bad RequestMalformed/invalid requestMissing required param, validation failure
404 Not FoundResource does not existLookup miss, often via an exception handler
500 Internal Server ErrorUnhandled server errorAn exception with no handler

Exam tip: Use ResponseEntity when the status code or headers vary at runtime; use @ResponseStatus when the status is fixed; and remember a plain object return is always 200 OK unless you override it with one of the other two.


Content Negotiation & Message Converters

HttpMessageConverter is the abstraction that converts between Java objects and the bytes of the HTTP body — in both directions. On the way in, a converter turns a @RequestBody into an object; on the way out, it serialises a @ResponseBody / ResponseEntity body. Which converters are registered determines what formats your endpoints can speak. With spring-boot-starter-web on the classpath, Spring Boot auto-configures Jackson and registers a MappingJackson2HttpMessageConverter, which is why JSON works with zero configuration.

Content negotiation is how Spring decides which representation to produce when several are possible. The primary mechanism is the request's Accept header: a client that sends Accept: application/json is asking for JSON, and Spring picks a converter that can produce it. The produces and consumes attributes on a mapping narrow this further — produces restricts which Accept values a handler will serve, and consumes restricts which Content-Type it will accept on the request body.

Content Negotiationthe Accept header selects the HttpMessageConverter
Accept headerSelected converterResponse
application/jsonMappingJackson2HttpMessageConverterJSON body
application/xmlMappingJackson2XmlHttpMessageConverter*XML body
text/plainStringHttpMessageConverterplain text
  • Spring matches the request Accept header against each converter's supported media types.
  • produces on @RequestMapping narrows what a handler will return — a mismatch yields 406 Not Acceptable.
  • consumes does the same for the request body's Content-Type — a mismatch yields 415 Unsupported Media Type.
  • *XML/JAXB converters require an extra dependency (jackson-dataformat-xml or JAXB); JSON via Jackson is auto-configured by spring-boot-starter-web.

Exam tip: Three facts the exam tests: no Jackson on the classpath ⇒ no automatic JSON (objects cannot be serialised to the body); a produces value that does not match the client's Accept406 Not Acceptable; a consumes value that does not match the request's Content-Type415 Unsupported Media Type.


Exception Handling

When a handler throws, Spring MVC needs to translate that exception into an HTTP response. There are three scopes of exception handling, from most local to most global.

// Per-controller handler
@RestController
public class BookApiController {
    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiError handleNotFound(BookNotFoundException ex) {
        return new ApiError(ex.getMessage());
    }
}
 
// Global handler across all controllers
@RestControllerAdvice              // = @ControllerAdvice + @ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(BookNotFoundException.class)
    public ResponseEntity<ApiError> handle(BookNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                             .body(new ApiError(ex.getMessage()));
    }
}

The three mechanisms:

  • @ExceptionHandler on a method handles exceptions thrown only by handlers in the same controller. It is the most local scope. Pair it with @ResponseStatus to set the status, or return a ResponseEntity for full control.
  • @ControllerAdvice / @RestControllerAdvice moves the same @ExceptionHandler methods into a separate class that applies globally, across every controller. This is how you build one consistent error-response format for the whole API.
  • Extending ResponseEntityExceptionHandler gives your @ControllerAdvice class Spring's built-in handling for the standard MVC exceptions (e.g. MethodArgumentNotValidException, HttpMessageNotReadableException). You override the protected hooks to customise the body while keeping the correct status codes.

Exam tip: @RestControllerAdvice = @ControllerAdvice + @ResponseBody. A plain @ControllerAdvice whose @ExceptionHandler returns an object treats that object as a view name — exactly the same @Controller-vs-@RestController trap, just one level up. Use @RestControllerAdvice for a REST API.


Calling REST Services with RestTemplate

So far we have built endpoints; the other half of "REST" on the exam is consuming them. RestTemplate is the synchronous HTTP client that the 2V0-72.22 exam tests, and it offers a method per common interaction.

RestTemplate rest = new RestTemplate();
 
// GET → deserialised object
Book book = rest.getForObject("/api/books/{id}", Book.class, 42);
 
// GET → full ResponseEntity (status + headers + body)
ResponseEntity<Book> resp = rest.getForEntity("/api/books/42", Book.class);
 
// POST → returns created body
Book created = rest.postForObject("/api/books", newBook, Book.class);
 
// Full control over method, headers and body
ResponseEntity<Book> ex = rest.exchange(
        "/api/books/42", HttpMethod.GET, new HttpEntity<>(headers), Book.class);

The methods you should recognise:

  • getForObject — performs a GET and returns just the deserialised body (no access to status or headers). URI template variables fill in from the trailing arguments.
  • getForEntity — performs a GET and returns the full ResponseEntity, so you can read the status code and response headers as well as the body.
  • postForObject — performs a POST with a request body and returns the deserialised response body (commonly the created resource).
  • exchange — the most flexible method: you pass the HTTP method, an HttpEntity (body + headers), and the expected response type, and you get a full ResponseEntity back. Use it whenever the convenience methods do not fit.

WebClient (from Spring WebFlux) is the modern, reactive successor and RestTemplate is in maintenance mode, but RestTemplate is the client 2V0-72.22 actually tests, so that is what to study here.

Exam tip: getForObject returns the body only; getForEntity returns the full ResponseEntity (status + headers + body); and exchange is the most flexible — it lets you set the HTTP method, headers, and body explicitly.


The View Layer (brief)

In a traditional server-rendered application a @Controller method returns a logical view name (a String like "home"), and a ViewResolver maps that name to a concrete View — for example a Thymeleaf template — which renders the response HTML. Data is passed to the view through the Model: you add attributes to a Model parameter, or use @ModelAttribute to populate model objects from the request, and the view reads those attributes when rendering.

For the 2V0-72.22 exam, the view layer is lightly tested — REST is the priority, and almost every web question is about JSON endpoints rather than HTML rendering. Know the concept, but do not over-invest here.

Exam tip: Returning a String from a plain @Controller (without @ResponseBody) is a view name, not the response body. This is the same distinction as @Controller vs @RestController, and it is the one view-related fact most likely to appear.


Common Exam Traps

#TrapWhat to remember
1@Controller returning a String for JSONWithout @ResponseBody/@RestController the String is a view name, not the body.
2@PathVariable vs @RequestParam/{id}@PathVariable; ?id=@RequestParam.
3No Jackson on the classpathNo auto JSON serialisation; objects can't be written to the body.
4produces mismatch with AcceptReturns 406 Not Acceptable.
5consumes mismatch with Content-TypeReturns 415 Unsupported Media Type.
6@RestControllerAdvice vs @ControllerAdvice@RestControllerAdvice = @ControllerAdvice + @ResponseBody.
7ResponseEntity vs @ResponseStatusResponseEntity for runtime-varying status/headers; @ResponseStatus for a fixed status.
8getForObject vs getForEntitygetForObject → body only; getForEntity → full ResponseEntity.
9@RequestBody on a GETGET has no body to bind; use @RequestParam/@PathVariable.
10Plain object return statusAlways 200 OK unless overridden by ResponseEntity/@ResponseStatus.

Test these with the practice question bank — Spring MVC questions appear under the "Spring MVC" topic.


Frequently Asked Questions

Is Spring MVC on the 2V0-72.22 exam?

Yes. Spring MVC carries roughly 10% of the questions (about 6 out of 60), and they are focused on REST controllers, request and response handling, and RestTemplate — not on server-side view rendering.

What is the DispatcherServlet?

It is the front controller of Spring MVC: the single entry point for every request. It delegates to a HandlerMapping to find the handler, a HandlerAdapter to invoke it, and then either an HttpMessageConverter (for REST responses) or a ViewResolver and View (for HTML). Spring Boot registers it automatically, mapped to /.

What is the difference between @Controller and @RestController?

@RestController is @Controller + @ResponseBody. With @RestController, a handler's return value is serialised straight into the response body (e.g. JSON); with a plain @Controller, a returned String is treated as a view name to be resolved and rendered.

When should I use @PathVariable vs @RequestParam?

Use @PathVariable to bind a URI template variable, such as the {id} in /books/{id}. Use @RequestParam to bind a query-string or form parameter, such as the id in /books?id=42. The shape of the URL tells you which one applies.

How do I set a custom HTTP status code?

Return a ResponseEntity when the status (or headers) must be decided at runtime — for example 201 Created on success and 404 Not Found on a miss. Use @ResponseStatus on the method (or on an exception class) when the status is always the same, such as 204 No Content for a delete.

What does @RestControllerAdvice do?

It provides global exception handling across all controllers, with the handler return values written as response bodies. It is @ControllerAdvice + @ResponseBody, so it is the right choice for a REST API where you want one consistent JSON error format.

Do I need to know WebClient for the exam?

No. The 2V0-72.22 exam tests the synchronous RestTemplate for calling REST services. WebClient is the modern reactive alternative from Spring WebFlux, and it is out of scope for this exam.


Next Steps

Spring Boot Testing Exam Guide@WebMvcTest, MockMvc, slicing the MVC layer in tests

Spring Security Exam Guide — how the servlet filter chain wraps your controllers

Spring Data JPA Exam Guide — the persistence layer behind your REST endpoints

How to Prepare — 8-Week Study Plan — pillar guide with topic weights

Spring Professional Practice Questions — free exam-style questions

Get Full Access — 970+ Practice Questions — the full exam bank including Spring MVC

Practice This Topic

Reinforce what you've learned with free practice questions and detailed explanations.