Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)Spring & Spring Boot Internals
✓ FreeAdvanced· 12 min read

Spring MVC & REST Internals, Exception Frameworks — Interview Questions

Five essential REST annotations (and @RequestMapping/@GetMapping/@PostMapping/@PathVariable), resolving overlapping and "ambiguous" mappings without changing URL or method, @RequestParam vs @PathVariable vs @ModelAttribute, multiple matching @ExceptionHandlers, custom statuses, content negotiation, HiddenHttpMethodFilter, global interception without filters, designing a custom exception-handling framework (Problem Details), creating custom annotations, securing DTOs, plus follow-ups on PUT vs POST bugs, @Qualifier conflicts, file uploads and cache refresh.

Published September 25, 2026


How to use this lesson

Senior MVC questions are about how the DispatcherServlet picks and invokes handlers, and how you design consistent API behaviour: errors, content types, validation. Know the extension points: HandlerMapping, HandlerAdapter, argument resolvers, message converters, HandlerInterceptor, and @ControllerAdvice.

Q1. Name five REST API annotations you use.

Short answer:

  1. @RestController: @Controller + @ResponseBody, so return values are serialised to the response body (JSON through Jackson).
  2. @RequestMapping: maps a path (and optionally the method, consumes, produces, params or headers) to a class or method. Usually used at class level for a base path.
  3. @GetMapping/@PostMapping/@PutMapping/@PatchMapping/@DeleteMapping: method-specific shortcuts for @RequestMapping(method = …).
  4. @PathVariable: binds a URI template variable (/orders/{id}).
  5. @RequestBody: deserialises the body. @RequestParam binds query or form parameters. @RequestHeader, @ResponseStatus, and @Valid/@Validated (validation) are also used constantly.
@RestController
@RequestMapping("/api/v1/orders")
class OrderController {
    @GetMapping("/{id}")
    OrderDto get(@PathVariable UUID id) { return service.get(id); }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    OrderDto create(@Valid @RequestBody CreateOrderRequest req, @RequestHeader("Idempotency-Key") String key) {
        return service.create(req, key);
    }
}

Learn it in depth → Building REST Controllers

Q2. What do @RequestMapping, @GetMapping, @PostMapping and @PathVariable each do, precisely?

Short answer:

  • @RequestMapping: registers RequestMappingInfo (paths, methods, params, headers, consumes, produces) with RequestMappingHandlerMapping. Class-level and method-level mappings are combined.
  • @GetMapping: a composed annotation, equivalent to @RequestMapping(method = GET). It's for reads: safe and idempotent.
  • @PostMapping: @RequestMapping(method = POST). It's for creation or commands: not idempotent unless you design for it.
  • @PathVariable: resolved by PathVariableMethodArgumentResolver from the matched URI template. It supports regex constraints ({id:\\d+}), required = false with optional paths, and type conversion.

Q3. Follow-up: have you seen real bugs caused by choosing PUT or POST wrongly? How did you fix them?

Short answer: The classic bug: creation over POST, retried by a client or gateway after a timeout, which creates duplicate orders or payments, because POST isn't idempotent. Fixes:

  • idempotency keys on POST (the client sends Idempotency-Key, and the server stores the key and the result, and returns the same result on retries);
  • PUT to a client-chosen ID (PUT /orders/{clientGeneratedId}), where repeated calls update the same resource;
  • unique constraints (for example, on an external reference) as the final safety net.

The opposite bug is using PUT for partial updates, which wipes fields the client didn't send. Use PATCH, or require the full representation, with ETags.

Q4. Follow-up: with several beans of the same type, how do you handle conflicts with @Autowired and @Qualifier? What goes wrong if they're mixed up?

Short answer: Injecting by type with several candidates throws NoUniqueBeanDefinitionException. Resolve it with:

  • @Qualifier("stripeGateway") at the injection point (matching a bean name or a custom qualifier annotation);
  • @Primary on the default implementation;
  • a Map<String, PaymentGateway> or List, and selection by key (the Strategy registry).

The pitfalls:

  • a misspelled qualifier gives NoSuchBeanDefinitionException at startup (it fails fast, which is good);
  • @Primary silently wins where a specific bean was intended, if someone forgets the qualifier;
  • qualifiers on @Bean methods vs on class-level components that don't match;
  • renaming a bean class changes its default bean name, and breaks string qualifiers. Prefer custom qualifier annotations (@Stripe), which are type-safe.

Q5. Follow-up: for file uploads, which kind of controller would you choose? When can @RestController complicate things?

Short answer: @RestController is the norm for upload APIs: @PostMapping(consumes = MULTIPART_FORM_DATA_VALUE) with @RequestPart MultipartFile file plus metadata. Configure spring.servlet.multipart.max-file-size/max-request-size, stream the file to storage (S3) instead of holding it in memory, validate the type and size, and scan for malware.

Where complications arise:

  • Large downloads or streaming responses: returning a byte[] from a @RestController loads everything into memory. Use ResponseEntity<Resource> or StreamingResponseBody, with explicit Content-Disposition, Content-Type and Content-Length headers.
  • Mixed HTML and file flows (a form view plus a download) suit @Controller, with selective @ResponseBody.
  • Very large uploads are better as pre-signed URLs: the client uploads directly to object storage, which bypasses the application entirely.

Q6. Follow-up: do you use a cache? When do you remove or refresh cached data?

Short answer: For example, product details and prices, cached with Caffeine plus Redis, using cache-aside:

  • Evict on write: @CacheEvict when a product is updated. Better still, after the transaction commits (@TransactionalEventListener), so readers never re-cache stale data.
  • TTLs as a safety net against missed invalidations.
  • Event-driven invalidation across instances: publish ProductUpdated (Kafka, or Redis pub/sub) so every node evicts its local copy.
  • Refresh-ahead for hot keys (Caffeine's refreshAfterWrite), which avoids latency spikes.
  • Versioned keys (product:v2:{id}) when the cached format changes.

Q7. How does Spring MVC resolve a request when several @RequestMappings overlap? How can two "ambiguous" URLs work without changing the URL or the HTTP method?

Short answer: RequestMappingHandlerMapping collects every mapping whose conditions match, then picks the most specific, comparing:

  1. patterns: fewer URI variables and wildcards is more specific, so /orders/latest beats /orders/{id}, and a literal beats a variable;
  2. then the params, headers, consumes and produces conditions, and the method.

If two remain equally specific, startup (or the request) fails with "Ambiguous handler methods mapped".

To disambiguate the same URL and method without changing either, add request conditions:

  • params: @GetMapping(path = "/reports", params = "type=summary") vs params = "type=detail", or params = "!type";
  • headers: headers = "X-API-Version=2";
  • consumes/produces: the same POST path, consuming application/json vs multipart/form-data, or producing application/json vs text/csv (content negotiation through the Accept header);
  • path-variable regex constraints: /{id:\\d+} vs /{slug:[a-z-]+}.
@GetMapping(path = "/items/{key}", produces = MediaType.APPLICATION_JSON_VALUE) ItemDto asJson(@PathVariable String key) { ... }
@GetMapping(path = "/items/{key}", produces = "text/csv")                        String asCsv(@PathVariable String key) { ... }
@GetMapping(path = "/items/{id:\\d+}")                                          ItemDto byId(@PathVariable long id) { ... }

Q8. What's the difference between @RequestParam, @PathVariable and @ModelAttribute?

Short answer:

  • @PathVariable: a value inside the URI path (/orders/{id}). It identifies a resource, and is required by default.
  • @RequestParam: a query string or form field (?page=2&status=PAID). It's for filters, options and pagination. It supports defaultValue, required = false, and multiple values (List<String>).
  • @ModelAttribute: binds many request parameters onto an object (OrderSearch with page, status and dateFrom fields), through data binding. It's also used on methods that add attributes to the model for views. For GET search forms, binding query parameters to a DTO keeps controller signatures clean. Add @Valid for validation.

Request bodies (JSON) use @RequestBody, not @ModelAttribute.

Q9. What happens if several @ExceptionHandler methods match the same exception?

Short answer: Spring picks the handler for the closest match in the exception's type hierarchy: the most specific exception class declared. For example, EntityNotFoundException beats RuntimeException, which beats Exception. Precedence across locations:

  1. handlers in the controller itself win over @ControllerAdvice handlers;
  2. among several advice beans, @Order/Ordered decides (the first matching advice wins);
  3. then the exception-depth rule applies within the chosen advice.

Two handlers for exactly the same exception type in one class gives IllegalStateException: Ambiguous @ExceptionHandler method at startup. You can also scope advice with @RestControllerAdvice(basePackages = …, assignableTypes = …, annotations = …).

Q10. Can you return a custom HTTP status from a @RestController method? How?

Short answer: Several ways:

  • ResponseEntity: ResponseEntity.status(HttpStatus.ACCEPTED).header("Location", url).body(dto), which is dynamic per call.
  • @ResponseStatus(HttpStatus.CREATED) on the method, for a fixed status.
  • @ResponseStatus on a custom exception class, or ResponseStatusException (throw new ResponseStatusException(HttpStatus.CONFLICT, "version mismatch")).
  • @ExceptionHandler returning ProblemDetail or ResponseEntity, which gives centralised mapping (the best for consistency).
  • Injecting HttpServletResponse and calling setStatus (rarely needed).

Q11. How does Spring MVC support content negotiation (JSON, XML and so on)?

Short answer:

  1. The ContentNegotiationManager determines the requested media types, primarily from the Accept header (a URL suffix and a format query parameter can be enabled, and are off or deprecated by default).
  2. Spring intersects them with the handler's produces and the formats the registered HttpMessageConverters can write: Jackson JSON (the default), Jackson XML (if jackson-dataformat-xml is on the classpath), JAXB, String, and byte arrays.
  3. It picks the best match, or returns 406 Not Acceptable.
  4. For request bodies, the Content-Type selects the reader converter, or returns 415 Unsupported Media Type.

Customise it with WebMvcConfigurer.configureContentNegotiation/extendMessageConverters, or by adding converters (for example CSV or Protobuf).

Q12. What is HiddenHttpMethodFilter, and why is it used for forms?

Short answer: HTML forms only support GET and POST. HiddenHttpMethodFilter lets a form POST a hidden field _method=PUT|PATCH|DELETE. The filter wraps the request, so Spring MVC sees the intended method, and routes it to @PutMapping/@DeleteMapping handlers. It's used in server-rendered applications (Thymeleaf). In Boot, it's disabled by default (spring.mvc.hiddenmethod.filter.enabled=true turns it on). REST clients and SPAs send real HTTP methods, so they don't need it.

Q13. How can you intercept requests globally without using a servlet filter?

Short answer:

  • HandlerInterceptor (registered through WebMvcConfigurer.addInterceptors, with path patterns): preHandle (it can reject), postHandle and afterCompletion. It runs inside the DispatcherServlet, and knows the chosen handler. Good for authentication checks, locale, timing, and MDC setup.
  • @ControllerAdvice with @InitBinder/@ModelAttribute (runs before handler methods), or RequestBodyAdvice/ResponseBodyAdvice, to inspect or modify bodies (for example, wrapping responses, or signing them).
  • Spring AOP around controllers (@Around("within(@RestController *)")).
  • In WebFlux: WebFilter or HandlerFilterFunction.

Filters vs interceptors: servlet filters run before Spring MVC, for every request, including static resources and errors. They're the right place for security (Spring Security is a filter chain), CORS, compression, and request logging of raw traffic.

Q14. What are the best practices for a custom exception-handling framework in Spring Boot?

Short answer:

  • A domain exception hierarchy: a base ApplicationException, carrying a stable error code (ORDER_NOT_FOUND), with subclasses for NotFound, Conflict, BusinessRuleViolation and ExternalServiceFailure. Don't put HTTP status codes in the domain layer. Map them at the edge.
  • One @RestControllerAdvice (ordered), mapping:
    • domain exceptions to statuses;
    • validation exceptions (MethodArgumentNotValidException, ConstraintViolationException, HandlerMethodValidationException) to 400, with field errors;
    • security exceptions to 401 or 403 (handled by the security entry points);
    • unknown exceptions to 500, with a generic message.
  • The standard format: RFC 9457 Problem Details (ProblemDetail, and spring.mvc.problemdetails.enabled=true), with type, title, status, detail, instance, plus extensions: errorCode, traceId, and errors[].
  • Never leak internals: no stack traces, SQL or class names in responses. Log the full error server-side with the trace ID, and return the trace ID to the client.
  • Log levels: 4xx at WARN or INFO (client problems), 5xx at ERROR, and don't log the same exception at several layers.
  • Consistency across services: a shared starter providing the advice and error model. Document the errors in OpenAPI.
  • Test the mapping (MockMvc tests for each error type).
@RestControllerAdvice
class ApiExceptionHandler extends ResponseEntityExceptionHandler {           // gets Problem Details for MVC exceptions
    @ExceptionHandler(ApplicationException.class)
    ProblemDetail handle(ApplicationException ex, HttpServletRequest req) {
        ProblemDetail pd = ProblemDetail.forStatusAndDetail(ex.status(), ex.getMessage());
        pd.setTitle(ex.code().title());
        pd.setProperty("errorCode", ex.code().name());
        pd.setProperty("traceId", Span.current().getSpanContext().getTraceId());
        return pd;
    }
    @ExceptionHandler(Exception.class)
    ProblemDetail unexpected(Exception ex) {
        log.error("Unhandled error", ex);
        return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error");
    }
}

Learn it in depth → Global Exception Handling

Q15. How do you create a custom annotation, and make it do something in Spring?

Short answer: Declaring it is easy:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)          // must be RUNTIME to be visible to Spring or reflection
@Documented
public @interface RateLimited {
    int permitsPerMinute() default 60;
    String key() default "";                  // for example, a SpEL expression for the per-user key
}

An annotation does nothing by itself. Give it behaviour through one of these:

  • an AOP aspect (@Around("@annotation(rateLimited)"));
  • a HandlerInterceptor that reads HandlerMethod.getMethodAnnotation(...);
  • a BeanPostProcessor (scanning beans at startup);
  • a Bean Validation constraint (@Constraint(validatedBy = ...));
  • meta-annotations: compose existing ones (@Transactional(readOnly = true) @Retention(RUNTIME) @interface ReadOnlyTx {}, which Spring honours through its merged-annotation support).

Choose @Target and @Retention deliberately, and add @Inherited or @Repeatable only when needed.

Q16. How do you secure DTOs?

Short answer: DTOs are the boundary contract, so secure them on the way in and out:

  • Input:
    • Use dedicated request DTOs, never entities, to prevent mass assignment (a client setting role or balance). Include only the fields the client may set.
    • Validate every field (Bean Validation: sizes, patterns, ranges), and reject unknown properties for sensitive endpoints (FAIL_ON_UNKNOWN_PROPERTIES).
    • Normalise strings, and cap collection sizes.
  • Output:
    • Separate response DTOs exposing only what the caller may see (no password hashes, internal IDs or other users' data).
    • Use field-level authorisation (different views per role, or Jackson @JsonView with care).
    • Mask personal data (partial card numbers or emails).
    • Exclude sensitive fields from toString and logs (records print all their fields by default, so override toString).
  • Deserialisation safety: disable polymorphic default typing in Jackson (a gadget-chain risk), and use explicit @JsonTypeInfo with allow-lists.
  • Object-level authorisation: check that the resource IDs in DTOs belong to the caller (IDOR).

Follow-up questions this topic invites — and their answers

Q: What does the DispatcherServlet do, in order? A: It finds a handler through the HandlerMappings, runs the interceptors' preHandle, invokes the handler through a HandlerAdapter (argument resolvers, then the method, then return-value handlers and message converters), runs postHandle, handles exceptions through HandlerExceptionResolvers (including @ExceptionHandler), renders views if any, and finally runs afterCompletion.

Q: How do you add a custom argument type to controller methods? A: Implement HandlerMethodArgumentResolver (for example, resolving a CurrentUser parameter from the security context), and register it through WebMvcConfigurer.addArgumentResolvers.

Q: How do you version REST APIs in Spring MVC? A: With a path prefix (/api/v1), a header condition (headers = "X-API-Version=2"), or media-type versioning (produces = "application/vnd.acme.v2+json"). Spring Framework 7 adds first-class API versioning support.

Q: @RestControllerAdvice vs @ControllerAdvice? A: @RestControllerAdvice = @ControllerAdvice + @ResponseBody, so handler return values are written as response bodies (JSON), which suits REST APIs.

Previous

Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions

Next

Spring Security Advanced Internals — Interview Questions

AI Tutor

Lesson: Spring MVC & REST Internals, Exception Frameworks — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.