Spring Web MVC es un marco web ligero basado en Java que implementa el patrón de diseño MVC (Modelo-Vista-Controlador), orientado a solicitudes.
Flujo de procesamiento de solicitudes en Spring MVC
- El usuario envía una solicitud al
DispatcherServlet. - El
DispatcherServletconsulta alHandlerMappingpara determinar el controlador adecuado. - El
DispatcherServletutiliza elHandlerAdapterpara invocar el método del controlador. - El
HandlerAdapterejecuta el método de procesamiento del controlador. - El nombre lógico de la vista se pasa al
ViewResolver. - La vista se renderiza.
- El control se devuelve al
DispatcherServlet, que envía la respuesta al usuario.
Pasos esenciales para el desarrollo con Spring MVC
- Configurar el
DispatcherServletenweb.xmlpara interceptar solicitudes. - Definir el
HandlerMappingpara asignar solicitudes a controladores. - Configruar el
HandlerAdapterpara soportar varios tipos de controladores. - Implementra los controladores para manejar la lógica de negocio.
- Establecer el
ViewResolverpara reoslver nombres de vista a tecnologías de vista específicas.
Componentes principales de Spring MVC
- Controlador frontal:
DispatcherServlet - Mapeador de solicitudes:
HandlerMapping - Adaptador de controladores:
HandlerAdapter - Resolvedor de vistas:
ViewResolver - Controladores o controladores de página:
Controller - Validador:
Validator - Objeto de comando: Vincula parámetros de solicitud a un objeto.
- Objeto de formulario: Proporciona datos para visualización y envío en formularios.
Anotaciones frecuentes
@Controller: Marca una clase como controlador.@RequestMapping: Define reglas de mapeo de solicitudes a métodos del controlador; puede aplicarse a clases y métodos con parámetros comovalueymethod.@RequestParam: Vincula parámetros de solicitud a argumentos del método; soportavalue,required(si es falso, el tipo debe ser de referencia), ydefaultValue.@ModelAttribute: Vincula parámetros de solicitud a objetos de comando; puede usarse en parámetros de métodos para agregar datos al modelo, o en métodos no manejadores para inicialización de datos.@SessionAttributes: Especifica atributos delModelMapque deben almacenarse en la sesión; se aplica a nivel de clase.@RequestBody: Utilizado para enlace de datos en desarrollos AJAX con formato JSON; requiere que elContent-Typedel cliente seaapplication/json, de lo contrario puede generar error 415.
Configuración de Spring MVC en un proyecto
Archivo POM.xml
Se agregan dependencias para Spring MVC, JSTL y la API JSP de Tomcat.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>9.0.50</version>
</dependency>
Archivo springmvc-servlet.xml en WEB-INF
Define la configuración del contexto de Spring MVC.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.ejemplo"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/vistas/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:resources location="/recursos-estaticos/img" mapping="/recursos-estaticos/img/**"/>
</beans>
Archivo web.xml
Configura el servlet de Spring MVC y filtros necesarios.
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Aplicación Web de Ejemplo</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>filtroCodificacion</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filtroCodificacion</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Ejemplo básico: Manejo de diferentes tipos de respuesta
Controlador que demuestra redirecciones y reenvíos.
package com.ejemplo.controlador;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class EjemploControlador {
@RequestMapping("/inicio")
public String redirigirAPagina(HttpServletRequest solicitud) {
solicitud.setAttribute("mensaje", "Reenvío a página");
return "inicio";
}
@RequestMapping("/redirigirAccion")
public String redirigirAccion(HttpServletRequest solicitud) {
solicitud.setAttribute("mensaje", "Redirección a acción");
return "redirect:inicio";
}
@RequestMapping("/redirigirPaginaEstatica")
public String redirigirPaginaEstatica() {
return "redirect:/paginaEstatica.jsp";
}
@RequestMapping("/reenvioAccion")
public String reenvioAccion(HttpServletRequest solicitud) {
solicitud.setAttribute("mensaje", "Reenvío a acción");
return "forward:redirigirPaginaEstatica";
}
@RequestMapping("/reenvioVistaProtegida")
public String reenvioVistaProtegida(HttpServletRequest solicitud) {
solicitud.setAttribute("mensaje", "Reenvío a vista en WEB-INF");
return "vistaProtegida";
}
}
Vista JSP correspondiente.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Ejemplo Spring MVC</title>
</head>
<body>
<p>Hola, este es un ejemplo básico.</p>
<p>${mensaje}</p>
</body>
</html>
Ejemplo de operaciones CRUD con Spring MVC
Controlador para gestionar libros.
package com.ejemplo.controlador;
import com.ejemplo.servicio.LibroServicio;
import com.ejemplo.utilidad.Paginador;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/libro")
public class LibroControlador {
@Autowired
private LibroServicio servicioLibro;
@RequestMapping("/lista")
public String listar(Map<String, Object> modelo, HttpServletRequest solicitud) {
Paginador paginador = new Paginador();
paginador.setSolicitud(solicitud);
List<Map<String, Object>> libros = servicioLibro.listarPaginado(paginador);
modelo.put("listaLibros", libros);
modelo.put("paginador", paginador);
return "listaLibros";
}
@RequestMapping("/prepararFormulario")
public String prepararFormulario(Integer idLibro, HttpServletRequest solicitud) {
if (idLibro != null) {
Map<String, Object> libro = servicioLibro.obtenerPorId(idLibro);
solicitud.setAttribute("libro", libro);
}
return "editarLibro";
}
@RequestMapping("/agregar")
public String agregar(Map<String, Object> libro) {
servicioLibro.agregar(libro);
return "redirect:/libro/lista";
}
@RequestMapping("/eliminar/{idLibro}")
public String eliminar(@PathVariable Integer idLibro) {
servicioLibro.eliminar(idLibro);
return "redirect:/libro/lista";
}
@RequestMapping("/actualizar")
public String actualizar(Map<String, Object> libro) {
servicioLibro.actualizar(libro);
return "redirect:/libro/lista";
}
}
Vistas JSP para listar y editar libros.
<%-- listaLibros.jsp --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Lista de Libros</title>
<script>
function navegarAEditar(id) {
window.location.href = "${pageContext.request.contextPath}/libro/prepararFormulario?idLibro=" + id;
}
function eliminarLibro(id) {
window.location.href = "${pageContext.request.contextPath}/libro/eliminar/" + id;
}
</script>
</head>
<body>
<button onclick="window.location.href='${pageContext.request.contextPath}/libro/prepararFormulario'">Agregar Libro</button>
<table border="1">
<tr><th>ID</th><th>Nombre</th><th>Precio</th><th>Acciones</th></tr>
<c:forEach items="${listaLibros}" var="libro">
<tr>
<td>${libro.idLibro}</td>
<td>${libro.nombre}</td>
<td>${libro.precio}</td>
<td>
<button onclick="navegarAEditar(${libro.idLibro})">Editar</button>
<button onclick="eliminarLibro(${libro.idLibro})">Eliminar</button>
</td>
</tr>
</c:forEach>
</table>
<%-- Componente de paginación omitido por brevedad --%>
</body>
</html>
<%-- editarLibro.jsp --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Editar Libro</title>
<script>
function enviarFormulario(id) {
var formulario = document.getElementById("formLibro");
if (id) {
formulario.action = "${pageContext.request.contextPath}/libro/actualizar";
} else {
formulario.action = "${pageContext.request.contextPath}/libro/agregar";
}
formulario.submit();
}
</script>
</head>
<body>
<form id="formLibro" method="post">
<input type="hidden" name="idLibro" value="${libro.idLibro}">
<label>Nombre: <input type="text" name="nombre" value="${libro.nombre}"></label><br>
<label>Precio: <input type="text" name="precio" value="${libro.precio}"></label><br>
<button type="button" onclick="enviarFormulario('${libro.idLibro}')">Guardar</button>
</form>
</body>
</html>
Manejo de recursos estáticos en Spring MVC
Para servir archivos como imágenes, CSS o JavaScript, se configura en el archivo XML del servlet.
<mvc:resources location="/recursos-estaticos/img" mapping="/recursos-estaticos/img/**"/>
<mvc:resources location="/recursos-estaticos/css" mapping="/recursos-estaticos/css/**"/>
<mvc:resources location="/recursos-estaticos/js" mapping="/recursos-estaticos/js/**"/>
Los archivos se colocan en la carpeta correspondiente dentro de webapp, por ejemplo, webapp/recursos-estaticos/img/imagen.jpg.