Difference Between @RestController and @Controller in Spring (Deep Explanation + Examples)

Difference Between @RestController and @Controller in Spring (Deep Explanation)

One of the most common points of confusion for developers migrating from legacy Spring MVC applications to modern Spring Boot microservices is understanding the exact architectural boundary between @Controller and @RestController. While both annotations are utilized to handle incoming HTTP web requests, they are fundamentally designed for entirely different application architectures and response lifecycles.

In this deep-dive guide, we will explore not only the surface-level syntax differences but also the internal framework mechanics. We will trace how the DispatcherServlet routes requests differently for each annotation, examine the role of the ViewResolver versus the HttpMessageConverter, and look at practical examples of when to implement each in a production environment.

📺 Video Walkthrough:
▶ Watch YouTube Short


🔍 The Core Definitions

The Traditional @Controller

The @Controller annotation is a specialized version of the standard Spring @Component annotation. It was originally designed for classic Spring MVC applications where the server is responsible for rendering the full user interface. When a method inside a standard @Controller finishes executing, Spring expects it to return a String that represents the name of a view template (like a JSP file, a Thymeleaf template, or a FreeMarker view). The server then compiles that template into HTML and ships the complete web page back to the user's browser.

The Modern @RestController

As the industry shifted toward decoupled architectures—where frontends are built with React or Angular, and backends are deployed as distinct cloud-native microservices—the need for server-side HTML rendering diminished. The @RestController was introduced in Spring 4.0 as a convenience annotation to serve this new era of RESTful APIs. It explicitly tells the Spring framework that this class will not be rendering views. Instead, every single method inside this class will return pure data (typically serialized as JSON or XML) directly to the HTTP response body.


🆚 Side-by-Side Architectural Comparison

Architectural Feature @Controller @RestController
Primary Purpose MVC Architecture — Returns server-rendered views (HTML/JSP/Thymeleaf) REST API Architecture — Returns raw data (JSON/XML)
Annotation Composition Requires method-level @ResponseBody to bypass view rendering A meta-annotation combining @Controller + @ResponseBody
Typical Return Value A String indicating the logical View Name A Java Object (DTO or Entity) to be serialized
Internal Processing Engine Handled by the ViewResolver Handled by the HttpMessageConverter
Common Integrations Monolithic Web Apps, Admin Dashboards AWS API Gateway, Mobile Apps, Angular/React Clients

🧠 Internal Flow: The Request Lifecycle

How Does @Controller Work Under the Hood?

When a client sends an HTTP request to a standard MVC application, the request is intercepted by Spring's DispatcherServlet (the front controller). The servlet consults the handler mappings to find the appropriate @Controller method. Once the method executes its business logic, it returns a simple string (e.g., "home").

The DispatcherServlet then passes this string to the ViewResolver. The ViewResolver appends the configured prefix and suffix (e.g., `/WEB-INF/views/home.jsp`), locates the physical template file, renders the dynamic data into standard HTML, and streams that HTML back to the client.

How Does @RestController Work Under the Hood?

The lifecycle of a @RestController skips the view resolution phase entirely, which significantly streamlines the process. When the DispatcherServlet intercepts the request, it routes it to the controller method. Because the class is marked with @RestController, Spring knows that the returned Java object (like a UserDTO) should be written directly to the HTTP response stream.

Spring delegates this task to the HttpMessageConverter (most commonly the MappingJackson2HttpMessageConverter). Jackson inspects the Java object, serializes it into a formatted JSON string, sets the HTTP response header to application/json, and sends the payload back to the consuming client or microservice.

Architectural Insight: By skipping the ViewResolver, @RestController endpoints generally consume less memory and execute faster, making them ideal for high-throughput, cloud-native backend services.

📘 Code Examples in Practice

Example 1: The Traditional @Controller

In this example, the homePage method utilizes the Model object to pass data to a Thymeleaf template. Notice that if we suddenly need one of these endpoints to return JSON instead of a view, we are forced to explicitly add the @ResponseBody annotation to that specific method.

@Controller
public class HomeController {

    // This method triggers the ViewResolver to return an HTML page
    @GetMapping("/home")
    public String homePage(Model model) {
        model.addAttribute("title", "Welcome to the Dashboard");
        return "home"; // Resolves to home.html or home.jsp
    }

    // This method bypasses the ViewResolver to return raw text/JSON
    @GetMapping("/api/status")
    @ResponseBody
    public String getStatus() {
        return "System is running optimally.";
    }
}

Example 2: The Modern @RestController

Here, we apply @RestController at the class level. We no longer need to pollute our methods with @ResponseBody. Every object returned by these methods is automatically intercepted by Jackson and converted into JSON.

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

    @GetMapping("/hello")
    public String hello() {
        return "Hello from the REST API"; // Returns plain text
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        // Returns a Java object that Jackson converts to {"id":1, "name":"John"...}
        return new User(id, "John Doe", "john@example.com");
    }
}

🧩 Hidden Differences Most Developers Don’t Know

1. Exception Handling Strategies Vary

Error handling is fundamentally different between the two paradigms. In a standard @Controller, throwing an exception usually redirects the user to a generic error.html page. In a RESTful architecture, this is unacceptable—clients expect structured error payloads. Therefore, when using @RestController, developers must pair it with @RestControllerAdvice to ensure exceptions are mapped to consistent JSON error responses (like returning a custom `ApiError` object with a 404 status code).

2. Content Negotiation Limitations

While a @RestController is highly efficient at returning JSON or XML based on the client's Accept header, it completely loses the ability to perform server-side rendering. If you attempt to return the string "index" from a @RestController hoping to load index.html, the framework will literally just return the raw word "index" to the browser.


🎯 When Should You Use Which?

Opt for @Controller When:

  • Building traditional Monolithic web applications where the backend manages both the business logic and the presentation layer.
  • Developing internal admin dashboards using templating engines like Thymeleaf or JSP.
  • You need to handle form submissions that redirect the user to a new HTML confirmation page.

Opt for @RestController When:

  • Building backend microservices that communicate purely via JSON.
  • Serving data to decoupled frontend frameworks like Angular, React, Vue.js, or Svelte.
  • Building APIs that will be consumed by mobile applications (iOS/Android).
  • Exposing endpoints behind cloud API Gateways.

✅ Conclusion

The choice between @Controller and @RestController dictates the fundamental architecture of your Spring Boot application. If your goal is to generate HTML and manage the user interface from the server, stick with @Controller. However, if you are building modern APIs designed to serve raw data to decoupled clients and microservices, @RestController is the cleaner, more efficient standard.

🌐 Build REST APIs the Right Way in Spring Boot

Understanding the difference between @Controller and @RestController is essential for building clean and scalable Spring Boot applications. Explore these related topics to strengthen your REST API, validation, and interview preparation skills.

🔄 Returning JSON Responses in Spring Boot

Learn how REST controllers serialize Java objects into JSON responses.

🏷️ Spring Boot Annotations for REST APIs

Understand core REST annotations like @GetMapping, @PostMapping, and @RequestBody.

✅ REST API Validation Annotations

Validate request payloads sent to REST controllers.

🧩 Custom Validation Annotations

Create reusable custom validators for REST API inputs.

🚨 Global Exception Handling

Handle REST API errors consistently across controllers.

🧩 Spring Boot CRUD API Example

See @RestController usage in a real-world CRUD REST API.

📘 Spring Boot JPA Basics

Understand how REST controllers interact with JPA repositories.

🎓 Spring Boot Interview Questions (Freshers)

Common interview questions on @Controller vs @RestController.

💼 Spring Boot Interview Questions (2–5 Years)

Real-world interview discussions around REST API design choices.