Fundamentos del Framework Web Spring MVC
Spring MVC es un framework de aplicación web de la capa de presentación, basado en el patrón de diseño Modelo-Vista-Controlador (MVC). Se integra de forma nativa con el contenedor de IoC y el soporte para AOP del núcleo de Spring. Permite que clases POJO simples actúen como controladores mediante anotaciones, sin necesidad de implementar interfaces específicas.
Configuarción Básica y Flujo de Trabajo
El desarrollo con Spring MVC sigue un flujo de petición-respuesta y separa claramente las responsabilidades de la capa web. El componente central es el DispatcherServlet, que actúa como controlador frontal.
1. Configuración del Descriptor de Despliegue (web.xml)
Se debe declarar y configurar el DispatcherServlet. Es recomendable especificar la ubicación del archivo de configuración de Spring MVC mediante el parámetro de inicialización contextConfigLocation.
<servlet>
<servlet-name>mvcDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/application-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvcDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. Configuración del Contexto de Spring MVC
El archivo de configuración define la exploración de componentes y el resolutor de vistas.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="... schema locations ...">
<context:component-scan base-package="com.example.web.controllers"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
3. Implementación de un Controlador Simple
Un controlador se marca con la anotación @Controller. El método que maneja la petición se anota con @RequestMapping.
package com.example.web.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class WelcomeHandler {
@RequestMapping("/greet")
public String showGreetingPage() {
System.out.println("Manejador de saludo invocado.");
return "welcome"; // Resuelto a /WEB-INF/pages/welcome.jsp
}
}
La Anotación @RequestMapping y sus Atributos
La anotación @RequestMapping es fundamental para mapear peticiones HTTP a métodos de controlador. Puede aplicarse a nivel de clase y de método. Sus atributos permiten un mapeo preciso.
Mapeo por Método HTTP (method)
Restringe el tipo de verbo HTTP que puede invocar el método.
@Controller
@RequestMapping("/api/users")
public class UserApiController {
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(User newUser) {
// Lógica para crear usuario
return "result";
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getUserProfile(@PathVariable Long id) {
// Lógica para obtener usuario
return "profile";
}
}
Mapeo por Parámetros (params) y Cabeceras (headers)
Permite condiciones adicionales basadas en parámetros de consulta o cabeceras HTTP.
// Solo se ejecuta si la petición incluye ?source=internal
@RequestMapping(value = "/data", params = "source=internal")
public String fetchInternalData() { return "dataView"; }
// Solo se ejecuta si la cabecera 'X-Custom-Header' está presente
@RequestMapping(value = "/secure", headers = "X-Custom-Header")
public String secureEndpoint() { return "secureView"; }
Patrones Ant y Variables de Ruta (@PathVariable)
Soporta patrones de URL estilo Ant y la captura de segmentos de la ruta.
// Patrón Ant: /files/2024/*.pdf
@RequestMapping("/files/{year}/{filename:.+}")
public String serveFile(@PathVariable String year, @PathVariable String filename) {
System.out.println("Año: " + year + ", Archivo: " + filename);
return "download";
}
// Captura múltiples segmentos con **
@RequestMapping("/docs/**/summary")
public String showDocSummary() {
return "docSummary";
}
Estilo Arquitectónico REST y Filtro HiddenHttpMethodFilter
Spring MVC facilita la creación de APIs RESTful, donde los recursos se identifican por URL y las operacioens por verbos HTTP (GET, POST, PUT, DELETE).
Configuración del Filtro para Verbos HTTP
Dado que los formularios HTML solo soportan GET y POST, se utiliza un filtro para simular PUT y DELETE mediante un parámetro oculto.
<!-- En web.xml -->
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Ejemplo de Controlador RESTful
@RestController // Combina @Controller y @ResponseBody
@RequestMapping("/api/orders")
public class OrderRestController {
@GetMapping("/{orderId}")
public Order getOrder(@PathVariable String orderId) {
return orderService.findById(orderId);
}
@PostMapping
public ResponseEntity<Void> placeOrder(@RequestBody Order newOrder) {
orderService.save(newOrder);
return ResponseEntity.created(URI.create("/api/orders/" + newOrder.getId())).build();
}
@PutMapping("/{orderId}")
public ResponseEntity<Void> updateOrder(@PathVariable String orderId, @RequestBody Order orderData) {
orderService.update(orderId, orderData);
return ResponseEntity.ok().build();
}
@DeleteMapping("/{orderId}")
public ResponseEntity<Void> cancelOrder(@PathVariable String orderId) {
orderService.delete(orderId);
return ResponseEntity.noContent().build();
}
}
Obtención de Datos de la Petición en el Controlador
Spring MVC ofrece varias formas de acceder a los datos de la petición HTTP dentro de los métodos del controlador.
Uso de @RequestParam y @RequestHeader
@RequestMapping("/submit")
public String handleSubmission(@RequestParam(value = "user", defaultValue = "guest") String username,
@RequestParam("token") String accessToken,
@RequestHeader("User-Agent") String userAgent) {
System.out.println("Usuario: " + username + ", Agente: " + userAgent);
return "confirmation";
}
Enlace Automático a Objetos POJO
Los parámetros de la petición se pueden vincular automáticamente a las propiedades de un objeto Java.
public class RegistrationForm {
private String fullName;
private String email;
private Integer age;
// Getters y Setters
}
@PostMapping("/register")
public String processRegistration(RegistrationForm form) {
System.out.println("Registrando: " + form.getFullName());
return "registered";
}
Acceso a APIs Nativas de Servlet
Los métodos del controlador pueden declarar parámetros de tipos como HttpServletRequest, HttpServletResponse o HttpSession para acceso directo.
@RequestMapping("/info")
public String getClientInfo(HttpServletRequest req, HttpServletResponse resp) {
String remoteAddr = req.getRemoteAddr();
// ... lógica con la respuesta si es necesario
return "infoPage";
}
Preparación de Datos para la Vista (Modelo)
Existen varias estrategias para pasar datos del controlador a la vista.
Devolviendo un Objeto ModelAndView
@RequestMapping("/dashboard")
public ModelAndView showDashboard() {
ModelAndView mav = new ModelAndView("dashboardView"); // Nombre de la vista
mav.addObject("currentUser", userService.getCurrentUser());
mav.addObject("recentActivities", activityService.getRecent(10));
return mav;
}
Usando un Parámetro Map o Model
Spring inyecta automáticamente un modelo mutable en estos parámetros.
@RequestMapping("/products")
public String listProducts(Model uiModel) {
List<Product> productList = catalogService.getAllProducts();
uiModel.addAttribute("products", productList);
uiModel.addAttribute("pageTitle", "Catálogo de Productos");
return "productList";
}
// Alternativa con Map<String, Object>
@RequestMapping("/report")
public String generateReport(Map<String, Object> modelAttributes) {
modelAttributes.put("reportData", reportGenerator.run());
return "reportView";
}