Anotaciones @Controller y @RestController en Spring Boot

En este tutorial, veremos cuál es la diferencia entre @Controller y @RestController en Spring Boot, a través de ejemplos.

Introducción

En Spring Boot, la clase de controlador es responsable de procesar las solicitudes entrantes de la API REST, preparar un modelo y devolver la vista para que se represente como respuesta.

Las clases de controlador en Spring están anotadas por la anotación @Controller o @RestController. Estos marcan las clases de controlador como un controlador de solicitudes para permitir que Spring lo reconozca como un servicio RESTful durante el tiempo de ejecución.

En este tutorial, cubriremos la definición de las anotaciones @Controller y @RestController, sus casos de uso y la diferencia entre las dos anotaciones.

Si eres nuevo en Spring Boot, quizás también quieras consultar nuestra guía completa sobre [Cómo construir una API REST de Spring Boot](/construir-una-api-spring-boot-rest-con-la-guia-completa -de-java/).

Flujo de trabajo de la API REST de Spring Boot

Antes de definir las dos anotaciones, repasaremos rápidamente el flujo de trabajo de cómo Spring Boot maneja las solicitudes y procesos de la API REST y devuelve una respuesta:

Dispatcher servlet spring boot

Primero, la solicitud es recibida por DispatcherServlet, que es responsable de procesar cualquier solicitud de URI entrante y asignarla a sus controladores correspondientes en forma de métodos de controlador. Una vez que se ha ejecutado el método del controlador, el recurso se procesa como una respuesta que puede ser JSON o XML.

En el diagrama anterior, los dos procesos encapsulados en el rectángulo son los procesos realmente implementados por un desarrollador. El resto lo ejecutan los servicios de Spring que se ejecutan en segundo plano, incluido DispatcherServlet.

La anotación @Controller

La anotación @Controller es una especialización de la anotación del estereotipo genérico @Component, que permite reconocer una clase como un componente administrado por Spring.

La anotación @Controller amplía el caso de uso de @Component y marca la clase anotada como una capa comercial o de presentación. Cuando se realiza una solicitud, esto informará a DispatcherServlet para incluir la clase de controlador en la búsqueda de métodos asignados por la anotación @RequestMapping.

Ahora, declararemos el controlador real para definir la lógica comercial y manejar todas las solicitudes relacionadas con el modelo Tree.

Primero, marque la clase con la anotación @Controller junto con @RequestMapping y especifique la ruta a /api/tree:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Controller
@ResponseBody
@RequestMapping("/api/tree")
public class TreeController {

    @Autowired
    private TreeRepository repository;
 
    @GetMapping("/{id}")
    public Tree getTreeById(@PathVariable int id) {
        return repository.findById(id);
    }
  
    @GetMapping
    public Tree getTreeById(@RequestParam String name, 
                            @RequestParam int age) {
        return repository.findFirstByCommonNameIgnoreCaseAndAge(name, age);
    }
}

La anotación @Autowired se usa para inyectar automáticamente dependencias del tipo especificado en el bean actual. En este caso, el bean TreeRepository se inyecta como una dependencia de TreeController.

@GetMapping es un atajo para @RequestMapping(method = RequestMethod.GET), y se utiliza para asignar solicitudes HTTP GET a los métodos del controlador asignado.

Hemos aplicado una anotación @ResponseBody al nivel de clase de este controlador. Cuando los controladores de solicitudes devuelvan datos, como return repository.findById(), la respuesta se serializará en JSON antes de devolverla al cliente.

Alternativamente, podría haber anotado cada tipo de respuesta con la anotación @ResponseBody en su lugar:

1
2
3
4
 @GetMapping("/{id}")
    public @ResponseBody Tree getTreeById(@PathVariable int id) {
        return repository.findById(id);
    }

Si ejecutamos esta aplicación, asumiendo que ya tenemos una instancia Tree guardada en la base de datos, con la ID de 1, y llegamos al punto final localhost:8080/1, nos recibirá con:

1
{"species":"Salix babylonica","commonName":"Weeping willow", "age":"150"}

Debido a la anotación @ResponseBody, los campos del objeto obtenido se serializan en JSON y se devuelven al cliente que lo solicitó.

La anotación @RestController

La anotación @RestController en Spring es esencialmente solo una combinación de @Controller y @ResponseBody. Esta anotación se agregó durante Spring 4.0 para eliminar la redundancia de declarar la anotación @ResponseBody en su controlador.

¡Esa es una declaración de anotación menos! Si también observa la definición de interfaz de las dos anotaciones para ver la diferencia entre las dos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
  //..
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
  //..
}

La interfaz RestController está anotada por @Controller y @ResponseBody en lugar de anotarla directamente con @Component.

Si reemplazamos la anotación de nuestro controlador con @RestController, no necesitaremos cambiar el dominio y la capa de persistencia ya que seguirán siendo compatibles con esta anotación.

Usando el controlador de ejemplo TreeController anterior, comparemos los cambios cuando usamos esta anotación:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@RestController
@RequestMapping("/api/tree")
public class TreeController {

    @Autowired
    private TreeRepository repository;
 
    @GetMapping("/{id}")
    public Tree getTreeById(@PathVariable int id) {
        return repository.findById(id);
    }
  
    @GetMapping
    public Tree getTreeById(@RequestParam String name, 
                            @RequestParam int age) {
        return repository.findFirstByCommonNameIgnoreCaseAndAge(name, age);
    }
}

Ahora, todos los métodos tienen aplicada la anotación @ResponseBody, ya que @RestController la aplica a nivel de clase.

Si ejecutamos esta aplicación, asumiendo que ya tenemos una instancia Tree guardada en la base de datos, con la ID de 1, y llegamos al punto final localhost:8080/1, nos recibirá con:

1
{"species":"Salix babylonica","commonName":"Weeping willow", "age":"150"}

Conclusión

Esencialmente, @RestController amplía las capacidades de las anotaciones @Controller y @ResponseBody.

Aparte del hecho de que @RestController existe para permitir que los controladores Spring sean una línea más cortos, no hay grandes diferencias entre las dos anotaciones.

La función principal de ambas anotaciones es permitir que una clase se reconozca como un componente administrado por Spring y permitir el manejo de solicitudes HTTP utilizando la API REST. REST.