Skip to main content

Spring MVC — Complete Guide

Spring MVC is the web framework within Spring that implements the Model-View-Controller design pattern, providing a structured way to build web applications and RESTful services.


What Is Spring MVC?

Spring MVC is a module of the Spring Framework used to create web applications. It organizes the application into three parts:

  • Model — The data and business logic
  • View — The user interface (HTML, JSON, XML)
  • Controller — The logic that handles requests and connects Model to View

This separation makes applications easier to develop, test, and maintain.


Why Use Spring MVC?

Problems It Solves

ProblemHow Spring MVC Fixes It
Tight coupling between request handling and business logicMVC pattern separates concerns cleanly
Manual servlet configurationDispatcherServlet handles request routing automatically
No standard way to map URLs to handler methods@RequestMapping and HTTP-method annotations
Complex form handling and validation@ModelAttribute, @Valid, BindingResult
Hard to test web layerMockMvc provides first-class testing support
Boilerplate response serialization@RestController auto-serializes to JSON/XML

Core Benefits

  1. Clean Separation of Concerns — Model, View, and Controller are independent and testable.
  2. Annotation-Driven — Minimal XML; configure with @Controller, @RequestMapping, @GetMapping, etc.
  3. Flexible View Resolution — Supports JSP, Thymeleaf, FreeMarker, JSON, XML, and more.
  4. Built-in Validation — Integrates with Bean Validation (JSR 380) via @Valid.
  5. RESTful Support@RestController makes building REST APIs straightforward.
  6. Interceptors — Cross-cutting concerns (logging, auth) without modifying controllers.
  7. TestabilityMockMvc allows testing without starting a server.

Core Components

ComponentRole
DispatcherServletFront controller — receives all requests, dispatches to handlers
HandlerMappingMaps URLs to controller methods
ControllerProcesses the request, interacts with the model
ModelAndViewBundles model data and the view name
ViewResolverResolves a view name to an actual view implementation
ViewRenders the response (JSP, Thymeleaf, JSON, etc.)

Request Lifecycle

Understanding how a request flows through Spring MVC is essential:

1. Client sends HTTP request


2. DispatcherServlet receives the request


3. HandlerMapping finds the matching Controller method


4. Controller processes the request
- Interacts with Service / Repository layers
- Populates the Model with data
- Returns a view name (or response body for REST)


5. ViewResolver resolves the view name to an actual View


6. View renders the response (HTML, JSON, XML)


7. DispatcherServlet sends the response to the client

DispatcherServlet

The DispatcherServlet is the front controller of Spring MVC. It is the single entry point for all HTTP requests.

Responsibilities:

  • Receives all incoming requests
  • Consults HandlerMapping to find the right controller
  • Invokes the controller method
  • Uses ViewResolver to resolve the view
  • Sends the rendered response back to the client

WebApplicationContext

The DispatcherServlet uses a WebApplicationContext — a specialized Spring application context for web applications that stores and manages web-specific components (controllers, view resolvers, handler mappings, interceptors).

Configuration (Spring Boot)

In Spring Boot, DispatcherServlet is auto-configured. No web.xml is needed. You can customize it via properties:

spring:
mvc:
servlet:
path: /api # Changes the servlet path

Java-Based Setup (Without Spring Boot)

public class WebAppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) {
AnnotationConfigWebApplicationContext context =
new AnnotationConfigWebApplicationContext();
context.register(WebConfig.class);

DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration =
servletContext.addServlet("dispatcher", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}

Controllers

@Controller vs @RestController

// Returns views (HTML pages)
@Controller
public class PageController {

@GetMapping("/home")
public String home(Model model) {
model.addAttribute("message", "Welcome!");
return "home"; // Resolved by ViewResolver to a template
}
}

// Returns data directly (JSON/XML) — combines @Controller + @ResponseBody
@RestController
@RequestMapping("/api/users")
public class UserApiController {

@GetMapping
public List<User> getAllUsers() {
return userService.findAll(); // Serialized to JSON automatically
}

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
Feature@Controller@RestController
ReturnsView names (HTML pages)Data (JSON/XML) directly
@ResponseBody needed?Yes, on each methodNo, implicit
Use caseTraditional web apps with templatesRESTful APIs

Request Mapping Annotations

@RestController
@RequestMapping("/api/orders") // Base path for all methods
public class OrderController {

@GetMapping // GET /api/orders
public List<Order> list() { ... }

@GetMapping("/{id}") // GET /api/orders/42
public Order get(@PathVariable Long id) { ... }

@PostMapping // POST /api/orders
public Order create(@RequestBody @Valid OrderRequest req) { ... }

@PutMapping("/{id}") // PUT /api/orders/42
public Order update(@PathVariable Long id, @RequestBody OrderRequest req) { ... }

@DeleteMapping("/{id}") // DELETE /api/orders/42
public void delete(@PathVariable Long id) { ... }
}

@RequestMapping Attributes

AttributePurpose
value / pathURL pattern
methodHTTP method (GET, POST, PUT, DELETE)
paramsRequired request parameters
headersRequired HTTP headers
consumesContent types the method accepts
producesContent types the method returns

Request Parameters and Data Binding

@PathVariable

Extracts values from the URL path:

@GetMapping("/users/{userId}/orders/{orderId}")
public Order getOrder(@PathVariable Long userId, @PathVariable Long orderId) {
return orderService.findByUserAndId(userId, orderId);
}

@RequestParam

Extracts query parameters:

// GET /search?query=spring&page=2&size=10
@GetMapping("/search")
public Page<Article> search(
@RequestParam String query,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return articleService.search(query, PageRequest.of(page, size));
}

@RequestBody

Deserializes the HTTP request body to a Java object:

@PostMapping("/users")
public User create(@RequestBody @Valid CreateUserRequest request) {
return userService.create(request);
}

@ModelAttribute

Binds form data to a Java object:

@PostMapping("/register")
public String register(@ModelAttribute @Valid RegistrationForm form,
BindingResult result) {
if (result.hasErrors()) {
return "register"; // Return to form with errors
}
userService.register(form);
return "redirect:/login";
}

Form Validation

Spring MVC integrates with Bean Validation (JSR 380):

public class RegistrationForm {

@NotBlank(message = "Username is required")
@Size(min = 3, max = 50, message = "Username must be 3-50 characters")
private String username;

@NotBlank(message = "Email is required")
@Email(message = "Must be a valid email")
private String email;

@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;

// getters and setters
}
@PostMapping("/register")
public String register(@Valid @ModelAttribute RegistrationForm form,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "register";
}
userService.register(form);
return "redirect:/success";
}

View Resolvers

A ViewResolver maps view names returned by controllers to actual view implementations.

Common ViewResolvers

ViewResolverPurpose
InternalResourceViewResolverResolves JSP views by adding prefix/suffix
ThymeleafViewResolverResolves Thymeleaf templates
FreeMarkerViewResolverResolves FreeMarker templates
BeanNameViewResolverResolves views by bean name
ContentNegotiatingViewResolverDelegates to other resolvers based on content type

InternalResourceViewResolver Example

@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}

With this configuration, returning "home" from a controller resolves to /WEB-INF/views/home.jsp.

ContentNegotiatingViewResolver

Automatically chooses the right view (JSON, XML, HTML) based on the client's Accept header. This allows a single endpoint to serve different response formats.


Interceptors

Interceptors execute code before and after a controller method, without modifying the controller itself.

HandlerInterceptor Interface

public class LoggingInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
// Runs BEFORE the controller — return false to block the request
log.info("Request: {} {}", request.getMethod(), request.getRequestURI());
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) {
// Runs AFTER the controller but BEFORE the view is rendered
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
// Runs AFTER the view is rendered — cleanup
}
}

Registering Interceptors

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/css/**");
}
}

Interceptor vs Servlet Filter

AspectInterceptorFilter
ScopeSpring MVC onlyEntire servlet container
Access to SpringFull access to Spring beans and contextLimited
GranularityCan target specific URL patterns easilyApplied to all requests
Use caseLogging, auth checks, locale switchingSecurity, compression, encoding

Exception Handling

@ExceptionHandler (Controller-Level)

@Controller
public class UserController {

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleNotFound(UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
}

@ControllerAdvice (Global)

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(404, ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.toList();
return ResponseEntity.badRequest()
.body(new ErrorResponse(400, "Validation failed", errors));
}
}

Static Resources

Serving CSS, JavaScript, Images

In Spring Boot, static files in src/main/resources/static/ are served automatically. Custom configuration:

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**")
.addResourceLocations("classpath:/static/assets/")
.setCachePeriod(3600);
}
}

Useful Annotations Summary

AnnotationPurpose
@ControllerMarks a class as a web controller (returns views)
@RestController@Controller + @ResponseBody (returns data)
@RequestMappingMaps URLs to handler methods
@GetMapping / @PostMapping / @PutMapping / @DeleteMappingHTTP-method-specific shortcuts
@PathVariableExtracts values from URL path
@RequestParamExtracts query parameters
@RequestBodyDeserializes request body
@ModelAttributeBinds form data to an object
@ValidTriggers bean validation
@ResponseBodySerializes return value to response body
@SessionAttributesStores model attributes in the HTTP session
@CookieValueExtracts cookie values
@ExceptionHandlerHandles exceptions in a controller
@ControllerAdviceGlobal exception handling across controllers
@InitBinderCustomizes data binding for a controller

Interview Questions

Q1: What is Spring MVC?

Spring MVC is a web framework within the Spring ecosystem that implements the Model-View-Controller pattern. It separates the application into Model (data), View (UI), and Controller (request handling logic). The DispatcherServlet acts as the front controller, routing requests to appropriate handlers.

Q2: Describe the lifecycle of a Spring MVC request.

  1. DispatcherServlet receives the HTTP request
  2. HandlerMapping finds the matching controller method
  3. The controller processes the request, interacts with services/repositories
  4. The controller returns a view name or response body
  5. ViewResolver resolves the view name to an actual view
  6. The view renders the response
  7. DispatcherServlet sends the response to the client

Q3: What role does the DispatcherServlet play?

DispatcherServlet is the front controller — the central entry point for all HTTP requests. It coordinates the entire request-processing pipeline: finding the handler, invoking it, resolving the view, and sending the response. All Spring MVC components (handler mappings, view resolvers, interceptors) are managed through it.

Q4: What is the WebApplicationContext?

WebApplicationContext is a specialized Spring application context for web applications. It extends ApplicationContext with web-specific features and stores web components like controllers, view resolvers, and interceptors. When a request arrives, DispatcherServlet uses it to locate and wire these components.

Q5: What are the differences between @Controller and @RestController?

@Controller is used for traditional web applications that return views (HTML pages). Methods need @ResponseBody to return data directly. @RestController combines @Controller and @ResponseBody, so every method automatically serializes its return value to JSON/XML. Use @Controller for template-based apps and @RestController for REST APIs.

Q6: Explain the @RequestMapping annotation and its attributes.

@RequestMapping maps HTTP requests to controller methods. Key attributes:

  • value/path: URL pattern
  • method: HTTP method (GET, POST, etc.)
  • params: Required request parameters
  • headers: Required HTTP headers
  • consumes: Accepted content types
  • produces: Response content types

Shortcut annotations (@GetMapping, @PostMapping, etc.) are preferred for readability.

Q7: How does Spring MVC handle form data and validation?

Form data is bound to objects using @ModelAttribute. Validation is applied using @Valid with Bean Validation annotations (@NotBlank, @Size, @Email). Errors are captured in BindingResult. If errors exist, the controller returns the form view with error messages. Individual parameters can be bound with @RequestParam.

Q8: What is ViewResolver and how does it work?

A ViewResolver maps logical view names (returned by controllers) to actual view implementations. The InternalResourceViewResolver adds a prefix and suffix to the name — e.g., "home" becomes /WEB-INF/views/home.jsp. Other resolvers handle Thymeleaf, FreeMarker, and content negotiation. This decouples controllers from specific view technologies.

Q9: How are interceptors used in Spring MVC?

Interceptors implement HandlerInterceptor with three methods: preHandle() (before the controller), postHandle() (after the controller, before view rendering), and afterCompletion() (after view rendering). They are registered via WebMvcConfigurer.addInterceptors() and are used for cross-cutting concerns like logging, authentication, and locale switching.

Q10: What is the difference between a Spring MVC interceptor and a servlet filter?

Interceptors operate within the Spring MVC framework and have access to Spring beans and the handler method. Filters operate at the servlet container level and are more general-purpose. Interceptors can access HandlerMethod and ModelAndView; filters work only with ServletRequest and ServletResponse. Use interceptors for Spring-specific concerns and filters for low-level tasks like encoding and compression.

Q11: How do you handle exceptions in Spring MVC?

  • @ExceptionHandler: Handles exceptions in a single controller
  • @ControllerAdvice / @RestControllerAdvice: Global exception handling across all controllers
  • HandlerExceptionResolver: Programmatic exception resolution

Best practice: use @RestControllerAdvice with @ExceptionHandler methods for consistent error responses across the entire application.

Q12: What is the role of @PathVariable?

@PathVariable extracts values from the URL path — e.g., @GetMapping("/users/{id}") with @PathVariable Long id captures the ID from the URL. When designing URLs, use clear names ({userId} not {id}), keep paths simple and logical, and avoid conflicts between fixed and variable segments.

Q13: How does Spring MVC support data binding?

Spring MVC automatically binds HTTP request data to Java objects. @ModelAttribute binds form data, @RequestParam binds query parameters, and @RequestBody deserializes JSON/XML payloads. Custom binding rules can be added via @InitBinder methods and custom Converter/Formatter implementations.

Q14: What is the role of @SessionAttributes and @CookieValue?

@SessionAttributes stores model attributes in the HTTP session across multiple requests — useful for multi-step forms or wizards. @CookieValue reads values from HTTP cookies. Security considerations: never store sensitive data in sessions or cookies, use secure and HttpOnly flags on cookies, and protect against XSS attacks.

Q15: How do you test Spring MVC applications?

Use MockMvc to simulate HTTP requests without starting a server. @WebMvcTest sets up a minimal Spring context for controller testing. Use @MockBean to mock service dependencies. For integration tests, use @SpringBootTest with TestRestTemplate or WebTestClient. Mockito helps create test doubles for service layers.

Q16: What are the best practices for integration testing in Spring MVC?

  • Use @SpringBootTest for full application context tests
  • Use MockMvc or TestRestTemplate to simulate HTTP requests
  • Keep test environments isolated from production
  • Clean up test data after each test
  • Use @Transactional on test classes so database changes roll back automatically
  • Test both happy paths and error scenarios

Q17: How does Spring Security integrate with Spring MVC?

Spring Security plugs into Spring MVC through servlet filters that run before the DispatcherServlet. It is enabled with @EnableWebSecurity and configured via SecurityFilterChain beans. Security filters handle authentication and authorization before requests reach controllers. Method-level security (@PreAuthorize, @Secured) can be applied directly on controller or service methods.