# Spring Boot MVC
Spring Boot es un framework de código abierto basado en el ecosistema de Spring, diseñado para simplificar el desarrollo de aplicaciones Java, especialmente las aplicaciones web y de microservicios. Fue creado por Pivotal Software y ahora es parte de VMware.
Spring Boot es una herramienta poderosa que permite a los desarrolladores crear aplicaciones Java de manera rápida y eficiente, reduciendo la cantidad de configuración manual y mejorando la productividad.
Algunas de las características principales de Sprin Boot son:
- Creación de aplicaciones RESTful, MVC, entre otras
- Incluye un servidor Tomcat embebido
- Herramientas de desarrollo DevTools
- Disminución de errores
- Simplificación de dependencias
- Configuración automatica
## Estructura de un Proyecto Spring Boot
**1. Directorio Raíz del Proyecto**
- src/: Carpeta principal que contiene el código fuente y los recursos de la aplicación.
- pom.xml o build.gradle: Archivo de configuración de dependencias y del proyecto (Maven o Gradle).
- `README.md`: Archivo de documentación del proyecto.
- .gitignore: Archivo para especificar qué archivos/directorios deben ser ignorados por Git.
**2. Carpeta src**
- main/: Contiene el código fuente principal de la aplicación.
- test/: Contiene las pruebas del código de la aplicación.
**3. Dentro de src/main/**
- java/: Directorio que contiene el código fuente en Java.
- resources/: Contiene recursos estáticos y archivos de configuración.
Dentro de src/main/java/ se debe crear el paquete base de la aplicación. Ejemplo: `com/empresa/producto/`. Todos los paquetes y clases del proyecto se deben crear dentro de esta carpeta.
Este puede estar organizado de la siguiente manera:
```
├── com
│ └── tuempresa
│ └── tuproducto
│ ├── Application.java
│ ├── config # Configuración (seguridad, beans, CORS, etc.)
│ ├── controller # Controladores REST
│ ├── dto # Clases DTO para entrada/salida de datos
│ ├── entity # Entidades JPA (antes estaba como 'model')
│ ├── exception # Manejo de excepciones personalizadas
│ ├── mapper # Clases para mapear entre entidades y DTOs (opcional)
│ ├── repository # Interfaces de acceso a datos (JPARepository, etc.)
│ ├── service # Interfaces y clases de lógica de negocio
│ ├── util # Clases utilitarias y helpers
│ └── validator # Validaciones personalizadas (opcional)
```
# Componentes
Un componente en Spring es una clase que se anota con la anotación @Component, lo que indica que puede ser gestionada por el contenedor de Spring, también llamado ApplicationContext. Este contenedor se encarga de crear, configurar e inyectar dependencias entre los objetos de la aplicación (Inversión de Control o IoC).
Al anotar una clase con @Component, permite que Spring detecte automáticamente la clase durante el proceso de escaneo de componentes (@ComponentScan) y la registre como un bean en el contexto.
Además de @Component, existen anotaciones especializadas que cumplen el mismo rol, pero con semántica específica para diferentes capas de la arquitectura:
- @Service: Indica que la clase es un servicio, una capa lógica de negocio.
- @Repository: Indica que la clase es un repositorio, una capa de acceso a datos.
- @Controller: Indica que la clase es un controlador, una capa de presentación para manejar solicitudes web.

Fuente: Imagen de **Cecilio Álvarez Caules**, publicada en el artículo _"Spring @Bean vs @Component"_ del blog **Arquitectura Java** ([arquitecturajava.com](https://www.arquitecturajava.com/spring-bean-vs-component/#:~:text=Spring%20y%20Componentes%20(Objetos)%20Hay%20que%20recordar,igual%20manera%20@RestController%20hereda%20de%20@Controller%20./)).
# Controladores
Son componentes (normalmente clases) que gestionan las solicitudes HTTP entrantes y devuelven respuestas apropiadas. Se utilizan principalmente en aplicaciones web basadas en Spring MVC (Model-View-Controller) y juegan un papel crucial en la lógica de manejo de solicitudes y respuestas.
## @Controller
Se usa para marcar una clase como un controlador web en una aplicación Spring MVC tradicional.
```java
@Controller
public class MyController {
@GetMapping("/hello")
public String sayHello(Model model) {
model.addAttribute("message", "Hello, World!");
return "hello"; // nombre de la vista (por ejemplo, hello.jsp o hello.html)
}
}
```
## @RestController
Es una combinación de @Controller y @ResponseBody. Se usa para aplicaciones RESTful donde cada método devuelve datos directamente en el cuerpo de la respuesta, típicamente en formato JSON o XML.
```java
@RestController
public class MyRestController {
@GetMapping("/users")
public List<User> getAllUsers() {
return userService.getAllUsers();
}
}
```
## @ResponseBody
Indica que el tipo de resultado debe escribirse directamente en el cuerpo de la respuesta en cualquier formato que se especifique, como ***JSON*** o ***XML***. Cuando **NO** se utiliza @ResponseBody, Spring MVC interpreta que el valor devuelto por un método del controlador es el nombre de una vista que debe ser resuelta y renderizada.
```java
@Controller
public class MyController {
@GetMapping("/hello")
@ResponseBody
public String sayHello() {
return "Hello, World!";
}
}
```
# Spring MVC (Model-View-Controller)
Es un marco de trabajo dentro del ecosistema de Spring que facilita la creación de aplicaciones web y servicios web en Java. Sigue el patrón de diseño MVC, que separa la lógica de negocio, la lógica de presentación y el control del flujo de la aplicación en diferentes componentes. Este enfoque modular ayuda a gestionar la complejidad y mejora la mantenibilidad del código.
## Handlers
Son métodos dentro de un controlador que se mapean a una URL o endpoint específico para manejar solicitudes HTTP y realizar tareas específicas. Estos métodos se anotan con diversas anotaciones como @RequestMapping, @GetMapping.
## Métodos Específicos
Spring MVC proporciona anotaciones específicas que son derivadas de @RequestMapping y se usan para manejar diferentes tipos de solicitudes HTTP:
- @GetMapping: Maneja solicitudes HTTP GET.
- @PostMapping: Maneja solicitudes HTTP POST.
- @PutMapping: Maneja solicitudes HTTP PUT.
- @DeleteMapping: Maneja solicitudes HTTP DELETE.
- @PatchMapping: Maneja solicitudes HTTP PATCH.
```java
@RestController
public class MyRestController {
@GetMapping("/users")
public List<User> getAllUsers() {
// Lógica para obtener todos los usuarios
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
// Lógica para crear un nuevo usuario
}
@PutMapping(value = "/users/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
// Lógica para actualizar un usuario existente
}
@DeleteMapping(value = "/users/{id}")
public void deleteUser(@PathVariable Long id) {
// Lógica para eliminar un usuario
}
}
```
## @RequestMapping
Es una de las anotaciones más fundamentales en Spring MVC. Se utiliza dentro de los controladores para mapear solicitudes web a rutas específicas. Al aplicar @RequestMapping a una clase, se define una ruta base que se aplicará a todos los métodos de esa clase. Esto significa que cada método heredará esta ruta base como prefijo en su propia ruta de mapeo. Esta práctica es útil para organizar rutas relacionadas y evitar la repetición de la misma parte de la URL en cada método.
```java
@Controller
@RequestMapping("/api")
public class MyController {
@GetMapping("/hello")
public String sayHello() {
return "hello";
}
}
```
## Obtener Parámetros
### @RequestParam
Se utiliza para extraer los parámetros de una solicitud HTTP. Permite acceder a los valores de los parámetros de consulta (query parameters) que se envían en la URL para así poder utilizarlos dentro de los métodos del controlador.
```java
@GetMapping("/greet")
public String greet(@RequestParam String name) {
return "Hello, " + name;
}
```
***Llamadas al Endpoint***
- Sin Parámetro
GET /greet
Respuesta: "Hello, World"
- Con Parámetro
GET /greet?name=John
Respuesta: "Hello, John"
***Opciones avanzadas para el manejo de parámetros***
```java
@GetMapping("/greet")
public String greet(@RequestParam(required = false, defaultValue = "World") String name) {
return "Hello, " + name;
}
```
- required: si es false indica que el parámetro no es obligatorio. Si no se proporciona, no se lanzará una excepción. Si es true el parámetro es obligatorio.
- defaultValue: Especifica un valor predeterminado en caso de que el parámetro no esté presente en la solicitud.
***Múltiples Parámetros con @RequestParam***
```java
@GetMapping("/search")
public String search(
@RequestParam String query,
@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
return "Searching for: " + query + " on page " + page + " with page size " + size;
}
```
***Llamadas al Endpoint***
GET /search?query=spring&page=2&size=5
Respuesta: "Searching for: spring on page 2 with page size 5"
### HttpServletRequest
Sirve para proporcionar información sobre la solicitud HTTP que se recibe. Esta interfaz, parte del paquete `javax.servlet.http`, permite a los desarrolladores acceder a diversos detalles de la solicitud, lo que facilita la manipulación y el procesamiento de la misma dentro de los controladores de Spring.
***Funcionalidades de HttpServletRequest***
- Obtener parámetros de la URL
- Obtener detalles como el método HTTP (GET, POST, etc.)
- Acceder a las cabeceras HTTP
```java
@GetMapping("/example")
public String handleRequest(HttpServletRequest request) {
// Obtener el método HTTP
String method = request.getMethod();
// Obtener un parámetro de la solicitud
String param = request.getParameter("paramName");
// Obtener un header específico
String userAgent = request.getHeader("User-Agent");
return "Method: " + method + ", Param: " + param + ", User-Agent: " + userAgent;
}
```
***Llamadas al Endpoint***
GET /example?paramName=valor123
Respuesta: "Method: GET, Param: 123, User-Agent: Mozilla/5.0"
### @PathVariable
Se utiliza para extraer valores de la URL en los controladores. Específicamente, se usa para vincular una variable de la ruta de la URL a un parámetro del método en el controlador. Esto es útil cuando quieres capturar partes específicas de la URL y utilizarlas dentro de tu método.
Por ejemplo, en la siguiente URL
`http://tu-dominio.com/usuarios/123`
En el ejemplo anterior 123 podría ser el ID de un usuario. para capturar ese valor usando `@PathVariable` se hace de la siguiente manera:
```java
@RequestMapping("/usuarios/{id}")
public String obtenerUsuarioPorId(@PathVariable("id") String id) {
// Lógica para manejar el usuario con el ID dado
return "El ID del usuario es: " + id;
}
```
***Múltiples Parámetros con @PathVariable***
`http://tu-dominio.com/usuarios/123/pedidos/456`
123 podría ser el ID del usuario y 456 podría ser el ID del pedido. Se puede capturar ambos valores usando @PathVariable de la siguiente manera:
```java
@RequestMapping("/usuarios/{usuarioId}/pedidos/{pedidoId}")
public String obtenerPedidoPorUsuarioYPedidoId(@PathVariable("usuarioId") String usuarioId, @PathVariable("pedidoId") String pedidoId) {
// Lógica para manejar el pedido del usuario con los IDs dados
return "El ID del usuario es: " + usuarioId + " y el ID del pedido es: " + pedidoId;
}
```
**Nota:** En las APIs REST, es frecuente incluir parámetros en la URL para identificar recursos específicos. El @PathVariable se emplea para capturar esos valores de la URL y usarlos dentro del método del controlador.
### @RequestBody
Se utiliza para vincular el cuerpo de una solicitud HTTP a un objeto Java en un método de controlador. Esto es especialmente útil cuando se trabaja con APIs RESTful y se necesita recibir datos en el cuerpo de la solicitud, típicamente en formato JSON o XML.
Cuando se hace una solicitud HTTP (como POST o PUT) a un endpoint de una API, el cuerpo de la solicitud puede contener datos en formato JSON, XML, o incluso en formato de formulario. El `@RequestBody` permite que Spring convierta automáticamente esos datos en un objeto Java que puedes usar en tu método del controlador.
```java
@PostMapping
public User create(@RequestBody Usuario usuario) {
Usuario nuevoUsuario = new Usuario();
nuevoUsuario.setName(usuario.getName());
nuevoUsuario.setLastname(usuario.getLastname());
return nuevoUsuario;
}
```
## ResponseEntity
`ResponseEntity<T>` es una clase en Spring que representa una respuesta HTTP completa. Lo cual permite tener control total sobre:
- El cuerpo de la respuesta (body)
- El código de estado HTTP (como 200 OK, 201 Created, 404 Not Found, etc.)
- Los encabezados HTTP (headers) personalizados si los necesitas
Esta clase sirve para personalizar lo que el controlador (@Controller o @RestController) devuelve al cliente.
:::info
ResponseEntity solo puede envolver tipos objeto, no tipos primitivos. Esto se debe a que `ResponseEntity<T>` requiere un tipo genérico (T), que debe ser una clase, no un tipo primitivo.
:::
**Ejemplos prácticos**
- Devolver un objeto con estado 200 OK:
```java
@GetMapping("/saludo")
public ResponseEntity<String> saludo() {
return ResponseEntity.ok("Hoa desde Spring!");
}
```
- Devolver un objeto con estado 201 Created:
```java
@PostMapping("/usuarios")
public ResponseEntity<Usuario> crearUsuario(@RequestBody Usuario usuario) {
Usuario nuevo = servicio.guardar(usuario);
return ResponseEntity.status(HttpStatus.CREATED).body(nuevo);
}
```
- Devolver un error 404 Not Found:
```java
@GetMapping("/usuarios/{id}")
public ResponseEntity<Usuario> obtenerUsuario(@PathVariable Long id) {
Optional<Usuario> usuario = servicio.buscarPorId(id);
return usuario.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
```
## Flujo de Trabajo
1. **Cliente:** Realiza una solicitud HTTP (GET, POST, PUT, DELETE).
2. **Controlador**
- Recibe la solicitud.
- Mapea la solicitud a un método específico utilizando anotaciones como @GetMapping, @PostMapping, @PutMapping, @DeleteMapping.
- Interactúa con el servicio correspondiente para procesar la solicitud.
3. **Servicio**
- Contiene la lógica de negocio.
- Llama a los métodos del repositorio para realizar operaciones de CRUD.
4. **Repositorio**
- Interactúa directamente con la base de datos.
- Utiliza JPA/Hibernate para realizar operaciones de persistencia.
- Los repositorios pueden proporcionar métodos específicos para la manipulación de datos sin necesidad de escribir código SQL explícito.
5. **Modelo (Entidad)**
- Representa los datos que se almacenan y recuperan de la base de datos. Estas clases suelen estar mapeadas a tablas de bases de datos cuando se utiliza un ORM (Object-Relational Mapping) como Hibernate.
6. **Respuesta:** El controlador devuelve una respuesta en formato JSON o XML al cliente.

Fuente: Imagen de **Cecilio Álvarez Caules**, publicada en el artículo _"Spring @Service usando el patrón servicio"_ del blog **Arquitectura Java** ([arquitecturajava.com](https://www.arquitecturajava.com/spring-service-usando-el-patron-servicio/)).
# Manejo de Excepciones
## @ControllerAdvice
En Spring, la anotación @ControllerAdvice se utiliza para manejar excepciones a nivel global en una aplicación web. Esta anotación permite definir una clase que puede interceptar excepciones lanzadas por controladores (controladores anotados con @Controller o @RestController) y proporcionar una manera centralizada de manejar estas excepciones.
```java
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleException(ResourceNotFoundException ex) {
return new ResponseEntity<>("Not found!", headers, HttpStatus.NOT_FOUND);
}
}
```
En el ejemplo anterior la clase GlobalExceptionHandler está anotada con @ControllerAdvice, lo que la convierte en una clase global de excepciones para todos los controladores.
El método handleException está anotado con @ExceptionHandler para para manejar excepciones específicas lanzadas por métodos de un controlador, en este caso maneja las excepciones `ResourceNotFoundException` y devuelve una respuesta HTTP con estado 404.
Estos métodos pueden devolver una vista o cualquier tipo de objeto, en aplicaciones REST, lo mas común es devolver un objeto de tipo `ResponseEntity`, donde se devulve una respuesta HTTP con: código de estado, cabeceras y cuerpo. `ResponseEntity` proporciona una manera flexible de configurar estas partes de la respuesta y se usa comúnmente en controladores para devolver respuestas HTTP personalizadas.
# Interceptores
Los interceptores son componentes que permiten interceptar solicitudes HTTP antes de que lleguen a los controladores y después de que los controladores hayan procesado las solicitudes. Se utilizan principalmente para tareas transversales como el registro, la autenticación, la autorización, la manipulación de datos y la gestión de excepciones.
## Cómo funcionan los interceptores
Un interceptor en Spring Boot implementa la interfaz `HandlerInterceptor`, que tiene tres métodos principales:
- `preHandle()`: Se ejecuta antes de que la solicitud llegue al controlador. **Si este método devuelve true, la solicitud continúa hacia el controlador; Si devuelve false, la solicitud se detiene.**
- `postHandle()`: Se ejecuta después de que el controlador haya procesado la solicitud; **Pero antes de que se genere la vista.** Este método puede modificar el ModelAndView que se utilizará para renderizar la vista.
- `afterCompletion()`: Se ejecuta después de que se haya completado la solicitud y se haya generado la vista. Se utiliza para tareas de limpieza o registro final.
## Ejemplo de uso de un interceptor
1. Se crea una clase que implemente la interfaz `HandlerInterceptor`.
```java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre Handle method is Calling");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
```
2. Luego, se registra el interceptor en la configuración de Spring.
```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor);
}
}
```
## Incluir rutas y Excluir rutas
- Para incluir rutas específicas, se utiliza el método `addPathPatterns` cuando se registra el interceptor. Este método acepta una lista de patrones de rutas que deben coincidir con las solicitudes para que el interceptor sea aplicado.
- Para excluir rutas específicas, se utiliza el método `excludePathPatterns`. Este método también acepta una lista de patrones de rutas y asegura que el interceptor no se aplique a las solicitudes que coincidan con esos patrones.
Supongamos que queremos aplicar el interceptor a todas las rutas excepto a /login y /register, y queremos asegurarnos de que solo se aplique a las rutas que comienzan con /admin.
```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Incluir todas las rutas que comienzan con /admin
registry.addInterceptor(myInterceptor).addPathPatterns("/admin/**");
// Excluir rutas específicas
registry.addInterceptor(myInterceptor).excludePathPatterns("/admin/login", "/admin/register");
}
}
```
**Resumen**
- `addPathPatterns`: Especifica las rutas en las que el interceptor debe aplicarse.
- `excludePathPatterns`: Especifica las rutas que deben ser excluidas del interceptor.
## Argmuentos de los métodos interceptores
### HttpServletRequest
El objeto `HttpServletRequest` representa la solicitud HTTP que se envía desde el cliente (por ejemplo, un navegador web) al servidor. A través de este objeto se puede acceder a varios aspectos de la solicitud HTTP, como parámetros, encabezados, método HTTP, URL, atributos y sesión.
Ejemplo de uso: Se puede obtener los parámetros enviados con la solicitud (por ejemplo, datos de un formulario).
```java
String paramValue = request.getParameter("paramName");
```
### HttpServletResponse
El objeto `HttpServletResponse` representa la respuesta HTTP que se enviará desde el servidor al cliente. A través de este objeto se puede manipular la respuesta antes de que se envíe, para por ejemplo establecer el estado, modificar encabezados, escribir contenido, redirigir la solicitud y establecer el tipo de contenido.
Ejemplo de uso: Se puede establecer el tipo de contenido de la respuesta.
```java
response.setContentType("application/json");
```
### Objeto handler
El objeto `handler` en los métodos del interceptor representa el controlador (o método del controlador) que manejará la solicitud. Este objeto puede ser utilizado para obtener información adicional sobre el controlador y realizar acciones basadas en esa información. Generalmente, `handler` es una instancia de `HandlerMethod` en aplicaciones Spring MVC, que proporciona métodos para acceder a la clase y al método manejador.
Usos comunes del objeto handler
- Obtener el método del controlador: Puedes obtener el método del controlador que manejará la solicitud.
- Obtener la clase del controlador: Puedes obtener la clase del controlador.
- Acceder a las anotaciones presentes en el método o la clase del controlador.
Ejemplo de uso: Se podría usar el objeto `handler` en un interceptor para obtener el método del controlador que manejará la solicitud HTTP.
```java
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Obtener el método del controlador
Method method = handlerMethod.getMethod().getName();
System.out.println("Controller Method: " + method;
return true;
}
}
```
# Programación Orientada a Aspectos (POA)
Es un paradigma de programación que busca aumentar la modularidad al permitir la **separación de preocupaciones transversales (cross-cutting concerns)**. Estas preocupaciones son partes de un programa que afectan otras partes del mismo programa de manera amplia, como la gestión de logs, la seguridad, o la gestión de transacciones.
En la programación convencional, estas preocupaciones suelen entrelazarse con la lógica principal del programa, lo que puede dificultar el mantenimiento y la evolución del código. La POA aborda este problema permitiendo que estas preocupaciones se definan de manera separada en "aspectos". Los aspectos encapsulan comportamientos que afectan múltiples clases en un solo módulo, permitiendo que los desarrolladores mantengan y evolucionen estas funcionalidades de manera independiente.
## Componentes clave de la POA
- Aspectos: Módulos que encapsulan preocupaciones transversales. Pueden contener código para realizar tareas como logging, manejo de errores, etc.
- Join Points: Puntos en el flujo de ejecución del programa donde los aspectos pueden ser aplicados. Estos pueden ser llamados a métodos, instanciación de objetos, etc.
- Pointcuts: Expresiones que definen un conjunto de join points donde un consejo (advice) debe ser aplicado. Los pointcuts permiten seleccionar y agrupar múltiples join points en una única expresión, facilitando la aplicación de aspectos a múltiples lugares en el código.
- Advice: Código que se ejecuta en los join points especificados por los pointcuts. Puede ser antes, después, o alrededor de la ejecución del método original. Los advices son las partes concretas de código que implementan el comportamiento de algún Aspecto en puntos específicos del flujo de ejecución.
## Ejemplo de Programación Orientada a Aspectos (AOP)
Supongamos que queremos agregar logging a un conjunto de métodos en una aplicación. En un enfoque convencional, podríamos añadir llamadas de logging en cada uno de esos métodos. Con POA, definimos un aspecto que maneja el logging y especificamos a través de pointcuts cuáles métodos deben ser afectados.
```java
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Method called: " + joinPoint.getSignature().getName());
}
}
```
En este ejemplo, `@Aspect` define una clase como un aspecto, `@Before` define un advice que se ejecuta antes de la ejecución de los métodos seleccionados por el pointcut `execution(* com.example.service.*.*(..))`.
## Tipos de Advice
### Before Advice
Este tipo de advice se ejecuta antes de la ejecución del método objetivo. Es útil para tareas que necesitan realizarse antes de que un método específico comience, como validación de entradas, logging, o configuración de parámetros.
```java
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
```
### After (Finally) Advice
Este advice se ejecuta después de que el método objetivo ha terminado, sin importar si el método ha lanzado una excepción o no. Es ideal para tareas de limpieza, liberar recursos o logging general que debe ejecutarse siempre que el método termine.
```java
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
```
### After Returning Advice
Este tipo de advice se ejecuta después de que el método objetivo ha completado su ejecución exitosamente y ha devuelto un valor. Es útil para procesar o modificar el valor de retorno, o para realizar acciones basadas en el resultado del método.
```java
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("Method returned: " + result);
}
```
### After Throwing Advice
Este advice se ejecuta si el método objetivo lanza una excepción. Es útil para manejar excepciones, logging de errores, o para activar algún mecanismo de recuperación.
```java
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
System.out.println("Method threw an exception: " + error);
}
```
### Around Advice
Es el tipo de advice más flexible y poderoso, ya que rodea la ejecución del método objetivo. Esto permite controlar completamente cuándo se ejecuta el método, si se ejecuta, y qué hacer antes y después de su ejecución. Es útil para manejar transacciones, medir el tiempo de ejecución, o modificar el comportamiento del método.
```java
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around before method: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed(); // Ejecuta el método objetivo
System.out.println("Around after method: " + joinPoint.getSignature().getName());
return result;
}
```
#### ProceedingJoinPoint (Obtener Resultados Y Parámetros)
Es una interfaz en Spring AOP que se utiliza en un `Around Advice` para controlar la ejecución del método objetivo. Permite no solo ejecutar el método objetivo, sino también modificar su comportamiento, lo que lo convierte en una herramienta muy poderosa en la Programación Orientada a Aspectos.
**Funciones principales del ProceedingJoinPoint**
- Controlar la ejecución del método objetivo: Puedes decidir si el método objetivo se ejecuta o no, o incluso ejecutarlo múltiples veces.
- Obtener y modificar los parámetros del método: Puedes acceder a los parámetros que se pasan al método objetivo y, si es necesario, modificarlos antes de ejecutar el método.
- Obtener el resultado del método: Después de ejecutar el método objetivo, puedes acceder al valor de retorno y modificarlo antes de devolverlo al llamante.
- Capturar excepciones lanzadas por el método objetivo: Puedes manejar excepciones que el método objetivo pueda lanzar durante su ejecución.
**Ejemplo de Uso**
1. Obtener los parámetros del método objetivo
```java
@Aspect
public class LoggingAspect {
@Around("execution(* com.example.service.UserService.saveUser(..))")
public Object logParametersAndResult(ProceedingJoinPoint joinPoint) throws Throwable {
// Obtener los parámetros del método objetivo
Object[] args = joinPoint.getArgs();
// Mostrar los parámetros
System.out.println("Parámetros del método:");
for (Object arg : args) {
System.out.println(" - " + arg);
}
}
}
```
2. Mostrar el resultado del método
```java
@Aspect
public class LoggingAspect {
@Around("execution(* com.example.service.UserService.saveUser(..))")
public Object logParametersAndResult(ProceedingJoinPoint joinPoint) throws Throwable {
// Mostrar el resultado del método
System.out.println("Resultado del método: " + result);
// Devolver el resultado (sin modificarlo)
return result;
}
}
```
## Pointcuts
Como se menciono anteriormente un pointcut es una expresión que define un conjunto de joinpoints donde un aspecto debe aplicarse. Los Pointcuts permiten filtrar y seleccionar con precisión los puntos en el flujo del programa donde se quiere aplicar un aspecto.
Los Pointcuts en Spring AOP utilizan una sintaxis específica para definir y seleccionar puntos en el flujo de ejecución del programa. Es importante comprender esta sintaxis para usar los pointcuts de manera efectiva.
**Ejemplos**
1. Pointcut que captura todos los métodos en una clase específica
```java
@Around("execution(* com.example.demo.service.UserService.*(..))")
public void allMethodsInUserService() {}
```
2. Pointcut que captura todos los métodos con un nombre específico en cualquier clase
```java
@Around("execution(* getUserById(..))")
public void allGetUserByIdMethods() {}
```
3. Pointcut que captura métodos con un tipo de retorno específico
```java
@Around("execution(String com.example.demo.service.UserService.*(..))")
public void allStringReturnMethodsInUserService() {}
```
4. Pointcut que captura métodos anotados con una anotación específica
```java
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
```
### Desacoplamiento de Pointcuts
El desacoplamiento de pointcuts se refiere a la práctica de separar la definición de los pointcuts de la implementación del aspecto (Aspect), de manera que los pointcuts sean reutilizables, mantenibles y más fáciles de gestionar.
**Ejemplo**
Se definen los Pointcuts en un lugar en comun con la anotación `@Pointcut`
```java
public class PointcutConfig {
@Pointcut("execution(* com.example.demo.service.UserService.*(..))")
public void userServiceMethods() {}
}
```
Luego se reutiliza este Pointcut en varios aspectos con el nombre del método:
```java
@Aspect
@Component
public class LoggingAspect {
@Around("com.example.demo.config.PointcutConfig.userServiceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// Lógica de logging
return joinPoint.proceed();
}
}
```
```java
@Aspect
@Component
public class SecurityAspect {
@Before("com.example.demo.config.PointcutConfig.userServiceMethods()")
public void secureBefore(JoinPoint joinPoint) {
// Lógica de seguridad
}
}
```
## @Order
Cuando se aplican múltiples aspectos en una aplicación Spring, la anotación @Order puede ser utilizada para especificar el orden en el que se deben aplicar. Esto es útil cuando tienes varios aspectos y se necesita controlar cuál se ejecuta primero y cuál después.
```java
@Aspect
@Component
@Order(1) // Define el orden de ejecución de este aspecto
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// Código del advice
return joinPoint.proceed();
}
}
@Aspect
@Component
@Order(2) // Este aspecto se ejecutará después del LoggingAspect
public class SecurityAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object secureAround(ProceedingJoinPoint joinPoint) throws Throwable {
// Código del advice
return joinPoint.proceed();
}
}
```
# Definiciones
## Clases POJO (Plain Old Java Object)
Las clases POJO son clases Java simples que no siguen ninguna convención particular más allá de las convenciones estándar de Java. No implementan interfaces específicas ni extienden clases predefinidas. A menudo, los POJO se utilizan como clases de modelo o como parte de la capa de negocio.
## DAO (Data Access Object)
Los DAO proporcionan una abstracción para acceder a datos, usualmente encapsulando las operaciones CRUD (Crear, Leer, Actualizar, Eliminar). Son responsables de interactuar directamente con la base de datos usando consultas SQL explícitas algún otro mecanismo de acceso a datos.
## DTO (Data Transfer Object)
Los DTO se utilizan para transferir datos entre diferentes capas de una aplicación. Su propósito principal es transportar solo los datos que deben ser transferidos y no deben contener lógica de negocio. Los DTO son especialmente útiles cuando se trabaja con APIs RESTful o cuando se necesita transferir datos entre diferentes subsistemas de una aplicación.
# Bibliografía
Guzmán, A. (2024). Udemy. Spring Framework 6 & Spring Boot 3 Desde Cero a Experto 2024. *https://www.udemy.com/course/spring-framework-5/*