16. Introducción a Spring MVC
16.1. El papel de Spring MVC en una aplicación web
Situemos a Spring MVC en el desarrollo de una aplicación web. Por lo general, esta se construirá sobre una arquitectura de múltiples capas como la siguiente:
![]() |
- la capa [Web] es la que está en contacto con el usuario de la aplicación web. Este interactúa con la aplicación web a través de páginas web visualizadas por un navegador. Es en esta capa donde se encuentra Spring MVC y únicamente en esta capa;
- la capa [métier] implementa las reglas de gestión de la aplicación, tales como el cálculo de un salario o de una factura. Esta capa utiliza datos provenientes del usuario a través de la capa [Web] y de SGBD a través de la capa [DAO];
- La capa [DAO] (Objetos de acceso a datos), la capa [ORM] (Mapeador objeto-relacional) y el controlador JDBC gestionan el acceso a los datos de SGBD. La capa [ORM] actúa como puente entre los objetos manipulados por la capa [DAO] y las filas y columnas de las tablas de una base de datos relacional. Una especificación llamada JPA (Java Persistence API) permite abstraerse del ORM utilizado si este implementa dichas especificaciones. Este será el caso en este tutorial, por lo que de ahora en adelante nos referiremos a la capa ORM como la capa JPA;
- la integración de las capas la realiza el marco Spring;
16.2. El modelo de desarrollo de Spring MVC
Spring MVC implementa el modelo de arquitectura denominado MVC (Modelo – Vista – Controlador) de la siguiente manera:
![]() |
El procesamiento de una solicitud de un cliente se lleva a cabo de la siguiente manera:
- solicitud: las URL solicitadas tienen el formato http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&.... El [Front Controller] utiliza un archivo de configuración o anotaciones Java para «enrutar» la solicitud hacia el controlador adecuado y la acción correcta dentro de ese controlador. Para ello, utiliza el campo [Action] del URL. El resto del URL [/param1/param2/...] está formado por parámetros opcionales que se transmitirán a la acción. El C de MVC es aquí la cadena [Front Controller, Contrôleur, Action]. Si ningún controlador puede procesar la acción solicitada, el servidor web responderá que no se ha encontrado la acción solicitada.
- Procesamiento
- (continuación)
- La acción seleccionada puede utilizar los parámetros parami que le ha transmitido [Front Controller]. Estos pueden provenir de varias fuentes:
- de la ruta [/param1/param2/...] del URL,
- de los parámetros [p1=v1&p2=v2] de URL,
- de los parámetros enviados por el navegador junto con su solicitud;
- al procesar la solicitud del usuario, la acción puede requerir la capa [métier] [2b]. Una vez procesada la solicitud del cliente, esta puede generar diversas respuestas. Un ejemplo clásico es:
- una página de error si la solicitud no se pudo procesar correctamente
- una página de confirmación en caso contrario
- la acción solicita que se muestre una vista específica [3]. Esta vista mostrará datos que se denominan el modelo de la vista. Este es el M de MVC. La acción creará este modelo M [2c] y solicitará que se muestre una vista V [3];
- respuesta: la vista V seleccionada utiliza la plantilla M generada por la acción para inicializar las partes dinámicas de la respuesta HTML que debe enviar al cliente y, a continuación, envía dicha respuesta.
Para un servicio web / jSON, la arquitectura anterior se modifica ligeramente:
![]() |
- en [4a], el modelo, que es una clase de Java, se transforma en una cadena jSON mediante una biblioteca jSON;
- en [4b], esta cadena jSON se envía al navegador;
Ahora, aclaremos la relación entre la arquitectura web MVC y la arquitectura en capas. Dependiendo de la definición que se le dé al modelo, estos dos conceptos pueden estar relacionados o no. Tomemos como ejemplo una aplicación web Spring MVC de una sola capa:
![]() |
Si implementamos la capa [Web] con Spring MVC, tendremos una arquitectura web MVC, pero no una arquitectura multicapa. En este caso, la capa [web] se encargará de todo: presentación, lógica de negocio y acceso a los datos. Serán las acciones las que realicen este trabajo.
Ahora, consideremos una arquitectura web multicapa:
![]() |
La capa [Web] puede implementarse sin un marco de trabajo y sin seguir el modelo MVC. En este caso, sí tenemos una arquitectura multicapa, pero la capa web no implementa el modelo MVC.
Por ejemplo, en el entorno .NET, la capa [Web] mencionadapuede implementarse con ASP.NET y MVC, con lo que se obtiene una arquitectura en capas con una capa [Web] de tipo MVC. Una vez hecho esto, se puede reemplazar esta capa ASP.NET MVC por una capa ASP.NET clásica (WebForms) manteniendo el resto (de negocio, DAO, ORM) tal como está. De esta manera, se obtiene una arquitectura en capas con una capa [Web] que ya no es del tipo MVC.
En MVC, dijimos que el modelo M era el de la vista V, c.a.d, es decir, el conjunto de datos mostrados por la vista V. Se proporciona otra definición del modelo M de MVC:
![]() |
Muchos autores consideran que lo que se encuentra a la derecha de la capa [Web] forma el modelo M de MVC. Para evitar ambigüedades, se puede hablar de:
- del modelo del dominio cuando nos referimos a todo lo que se encuentra a la derecha de la capa [Web]
- del modelo de la vista cuando nos referimos a los datos que muestra una vista V
En lo sucesivo, el término «modelo M» se referirá exclusivamente al modelo de una vista V.
16.3. Un proyecto web / jSON con Spring MVC
El sitio [http://spring.io/guides] ofrece tutoriales de introducción para descubrir el ecosistema de Spring. Seguiremos uno de ellos para conocer la configuración de Maven necesaria para un proyecto de Spring MVC.
16.3.1. El proyecto de demostración
![]() |
- en [1], importamos una de las guías de Spring;
![]() |
- en [2], seleccionamos el ejemplo [Rest Service];
- en [3], elegimos el proyecto Maven;
- en [4], tomamos la versión final de la guía;
- en [5], validamos;
- en [6], el proyecto importado;
Los servicios web accesibles a través de URL estándar y que entregan texto jSON suelen denominarse servicios REST (REpresentational State Transfer). Un servicio se considera RESTful si cumple con ciertas reglas.
Ahora analicemos el proyecto importado, comenzando por su configuración de Maven.
16.3.2. Configuración de Maven
El archivo [pom.xml] es el siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- líneas 6-8: las propiedades del proyecto Maven. Falta una etiqueta [<packaging>] que indique el tipo de archivo generado por la compilación de Maven. Al no estar presente, se utiliza el tipo [jar]. Por lo tanto, la aplicación es una aplicación ejecutable de tipo consola, y no una aplicación web, en cuyo caso el empaquetado sería [war];
- líneas 10-14: el proyecto Maven tiene un proyecto padre [spring-boot-starter-parent]. Este es el que define la mayor parte de las dependencias del proyecto. Estas pueden ser suficientes, en cuyo caso no se agregan más, o no lo son, en cuyo caso se agregan las dependencias que faltan;
- líneas 17-20: el artefacto [spring-boot-starter-web] incluye las bibliotecas necesarias para un proyecto Spring MVC de tipo servicio web en el que no hay vistas generadas. Este artefacto incluye una gran cantidad de bibliotecas, entre ellas las de un servidor Tomcat integrado. Es en este servidor donde se ejecutará la aplicación;
Las bibliotecas que incluye esta configuración son muy numerosas:
![]() | ![]() |
Arriba se ven los tres archivos del servidor Tomcat.
16.3.3. La arquitectura de un servicio Spring [web / jSON]
Para un servicio web / jSON, Spring MVC implementa el modelo MVC de la siguiente manera:
![]() |
- en [4a], el modelo, que es una clase de Java, se transforma en la cadena jSON mediante una biblioteca jSON;
- en [4b], esta cadena jSON se envía al navegador;
16.3.4. El controlador C
![]() |
La aplicación importada cuenta con el siguiente controlador:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
- línea 9: la anotación [@RestController] convierte a la clase [GreetingController] en un controlador de Spring, es decir, que sus métodos se registran para procesar URL. Ya vimos la anotación similar [@Controller]. El resultado de los métodos de ese controlador era un tipo [String], que era el nombre de la vista que se debía mostrar. Aquí es diferente. Los métodos de un controlador de tipo [@RestController] devuelven objetos que se serializan para enviarlos al navegador. El tipo de serialización que se realiza depende de la configuración de Spring MVC. Aquí, se serializarán como jSON. Es la presencia de una biblioteca jSON en las dependencias del proyecto lo que hace que Spring Boot, mediante autoconfiguración, configure el proyecto de esta manera;
- línea 14: la anotación [@RequestMapping] indica el URL que procesa el método, en este caso el URL [/greeting];
- línea 15: ya hemos explicado la anotación [@RequestParam]. El resultado devuelto por el método es un objeto de tipo [Greeting].
- línea 12: un entero largo de tipo atómico. Esto significa que admite el acceso concurrente. Varios hilos pueden querer incrementar la variable [counter] al mismo tiempo. Esto se llevará a cabo de manera correcta. Un hilo solo puede leer el valor del contador si el hilo que lo está modificando ha terminado su modificación.
16.3.5. El modelo M
El modelo M generado por el método anterior es el siguiente objeto [Greeting]:
![]() |
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
La transformación jSON de este objeto creará la cadena de caracteres {"id":n,"content":"texto"}. Al final, la cadena jSON generada por el método del controlador tendrá la forma:
o
16.3.6. Ejecución
![]() |
La clase [Application.java] es la clase ejecutable del proyecto. Su código es el siguiente:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Ya hemos visto y explicado este código en el ejemplo anterior. Ejecutemos el proyecto:
![]() |
Se obtienen los siguientes registros de consola:
- línea 13: el servidor Tomcat se inicia en el puerto 8080 (línea 12);
- línea 17: el servlet [DispatcherServlet] está presente;
- línea 20: se ha detectado el método [GreetingController.greeting];
Para probar la aplicación web, se solicita el URL [http://localhost:8080/greeting]:
![]() | ![]() |
Se recibe correctamente la cadena jSON esperada. Puede ser interesante ver los encabezados HTTP enviados por el servidor. Para ello, utilizaremos la extensión de Chrome llamada [Advanced Rest Client] (Chrome / Ctrl-T / Menú [Applications] / [Advanced Rest Client] —véase el párrafo 23.11 de los anexos—):
![]() |
- en [1], el URL solicitado;
- en [2], se utiliza el método GET;
- en [3], la respuesta jSON;
- en [4], el servidor indicó que enviaba una respuesta en el formato jSON;
- en [5], se solicita el mismo URL, pero esta vez con un POST;
- en [7], la información se envía al servidor en el formato [urlencoded];
- en [6], el parámetro «name» con su valor;
- en [8], el navegador le indica al servidor que le está enviando información [urlencoded];
- en [9], la respuesta jSON del servidor;
16.3.7. Creación de un archivo ejecutable
Ahora crearemos un archivo ejecutable:
![]() |
![]() |
- en [1]: se ejecuta un objetivo de Maven;
- en [2]: hay dos objetivos (goals): [clean] para eliminar la carpeta [target] del proyecto Maven, y [package] para regenerarla;
- en [3]: la carpeta [target] generada se guardará en esta carpeta;
- en [4]: se genera el objetivo;
En los registros que aparecen en la consola, es importante que aparezca el complemento [spring-boot-maven-plugin]. Este es el que genera el archivo ejecutable (véase [pom.xml] a continuación):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Con una consola, nos desplazamos a la carpeta generada:
D:\Temp\wksSTS\gs-rest-service\target>dir
...
11/06/2014 15:30 <DIR> classes
11/06/2014 15:30 <DIR> generated-sources
11/06/2014 15:30 11 073 572 gs-rest-service-0.1.0.jar
11/06/2014 15:30 3 690 gs-rest-service-0.1.0.jar.original
11/06/2014 15:30 <DIR> maven-archiver
11/06/2014 15:30 <DIR> maven-status
...
- línea 5: el archivo generado;
Este archivo se ejecuta de la siguiente manera:
D:\Temp\wksSTS\gs-rest-service-complete\target>java -jar gs-rest-service-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.0.RELEASE)
2014-06-11 15:32:47.088 INFO 4972 --- [ main] hello.Application
: Starting Application on Gportpers3 with PID 4972 (D:\Temp\wk
sSTS\gs-rest-service-complete\target\gs-rest-service-0.1.0.jar started by ST in
D:\Temp\wksSTS\gs-rest-service-complete\target)
...
Ahora que la aplicación web está en ejecución, se puede acceder a ella con un navegador:
![]() |
16.3.8. Implementar la aplicación en un servidor Tomcat
Al igual que en el proyecto anterior, modificamos el archivo [pom.xml] de la siguiente manera:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
...
</project>
- línea 9: hay que indicar que se va a generar un archivo WAR (Web ARchive);
Además, hay que configurar la aplicación web. Al no existir el archivo [web.xml], esto se hace con una clase que hereda de [SpringBootServletInitializer]:
![]() |
La clase [ApplicationInitializer] es la siguiente:
package hello;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ApplicationInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
- línea 6: la clase [ApplicationInitializer] extiende la clase [SpringBootServletInitializer];
- línea 9: el método [configure] se redefine (línea 8);
- línea 10: se proporciona la clase que configura el proyecto;
Para ejecutar el proyecto, se puede proceder de la siguiente manera:
![]() |
- en [1-2], se ejecuta el proyecto en uno de los servidores registrados en el IDE de Eclipse;
Una vez hecho esto, se puede solicitar el URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] en un navegador:
![]() |
16.4. Conclusion
Hemos presentado un tipo de proyectos Spring MVC en los que la aplicación web envía un flujo jSON al navegador. Ahora desarrollaremos una aplicación web / jSON para publicar en la web la base de datos [dbproduitscategories] estudiada en los capítulos anteriores.























