We usually use the @Controller annotation to develop Spring MVC applications, but since version 4.x, Spring has introduced another annotation called @RestController. What is the purpose of annotation @RestController? What is the difference between @Controller annotation and @RestController annotation? In this tutorial, let’s find out!
First, I will create a new Maven project as an example:
If You do not know how to create this project, please follow the instructions of this tutorial.
The default dependencies have changed version as follows:
1 2 3 4 5 6 7 8 9 |
<properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <org.springframework-version>6.1.9</org.springframework-version> <org.slf4j-version>2.0.13</org.slf4j-version> <org.junit-version>5.10.2</org.junit-version> <jakarta.servlet-api-version>6.1.0</jakarta.servlet-api-version> </properties> |
To run this project, I will use the Maven Jetty Plugin:
1 2 3 4 5 |
<plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>11.0.21</version> </plugin> |
OK, let’s get started.
As you all know, in Spring MVC, the controller is the class that will be responsible for preparing the Model and selecting View to display the data in the Model to the View. In the example we just created, we have the HomeController class as a Controller class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.huongdanjava.springmvc; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate ); return "home"; } } |
It adds the “serverTime” data to the Model and selects View as the home.jsp file which is located in the “/src/main/webapp/WEB-INF/views” folder to render the interface.
The results will look like this:
Besides preparing the Model and selecting View, the Controller class can also write data directly into the response stream without having to select any View. To do this, in the method which is handling requests, we have to add another annotation, @ResponseBody.
For example, now, I add the @ResponseBody annotation to the home() method in the HomeController class as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) @ResponseBody public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate ); return "home"; } |
When running, you will no longer see results as before. Instead of the content of the home.jsp file with the Model data, the result is only “home” as the return portion of the home() method.
This is what we usually do to develop RESTful Web Service applications. We only return data in formats such as JSON, XML, … and no View is needed.
For the sake of developing RESTful Web Service applications, Spring has introduced the @RestController annotation from version 4.x. With the @RestController annotation replacing the @Controller annotation, we do not need to declare annotation @ResponseBody anymore.
Now if I replace @Controller annotation with @RestController annotation and remove annotation @ResponseBody in the home() method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.huongdanjava.springmvc; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * Handles requests for the application home page. */ @RestController public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate ); return "home"; } } |
The result is the same.
If you look at the code of the @RestController annotation, you will see clearly the combination.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Controller; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { @AliasFor( annotation = Controller.class ) String value() default ""; } |