Capítulo 1: Inversión de Control (IoC)
Introducción
¡Bienvenido! En este capítulo, usted va a comprender el principio fundamental que hace funcionar Spring.
Lo que va a aprender
- El problema del acoplamiento fuerte (y por qué es malo)
- La solución: la Inversión de Control
- Cómo Spring implementa IoC
- El Contenedor Spring y su papel
Duración: 30 minutos
El Problema: El Acoplamiento Fuerte
Situación Real
Imagine que está desarrollando una aplicación de comercio electrónico. Necesita enviar correos electrónicos a los clientes.
❌ Enfoque Tradicional (Sin IoC)
public class OrderService {
// Se crea directamente el objeto EmailService
private EmailService emailService = new EmailService();
public void processOrder(Order order) {
// Lógica de negocio
saveOrder(order);
// Enviar correo de confirmación
emailService.sendEmail(order.getCustomerEmail(), "Commande confirmée");
}
}
¿Por Qué Es Problemático?
Problema 1: Acoplamiento Fuerte
private EmailService emailService = new EmailService();
// ^^^^^^^^^^^^^^^^
// ¡Dependencia DIRECTA!
Consecuencia:
OrderServiceDEPENDE DIRECTAMENTE deEmailService- Imposible cambiar la implementación fácilmente
- Difícil de probar
Problema 2: Imposible Cambiar la Implementación
Escenario: Quiere pasar de Gmail a SendGrid para enviar los correos electrónicos.
// Debe MODIFICAR OrderService
public class OrderService {
// Antes
private EmailService emailService = new EmailService();
// Después - ¡HAY QUE CAMBIAR EL CÓDIGO!
private SendGridEmailService emailService = new SendGridEmailService();
// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
}
Problema: ¡Modificar código que funciona = riesgo de errores!
Problema 3: Imposible de Probar
Quiere probar OrderService sin enviar correos reales:
@Test
public void testProcessOrder() {
OrderService service = new OrderService();
Order order = new Order(...);
service.processOrder(order);
// PROBLEMA: ¡Se enviará un correo real!
// Imposible "mockear" EmailService
}
Resumen del Problema
OrderService ──────────> EmailService
(dependencia directa)
❌ Acoplamiento fuerte
❌ Difícil de modificar
❌ Difícil de probar
❌ Código rígido y frágil
La Solución: Inversión de Control (IoC)
Principio Básico
"¡No cree sus dependencias, pídalas!"
En vez de:
private EmailService emailService = new EmailService(); // ❌ YO creo
Haga:
private EmailService emailService; // ✅ ALGUIEN me lo da
¿Qué es IoC?
Inversión de Control (IoC) = Invertir quién controla la creación de los objetos
Antes de IoC (Control Normal)
OrderService dice: "Necesito un EmailService"
OrderService dice: "Lo voy a crear yo mismo con 'new'"
Con IoC (Control Invertido)
OrderService dice: "Necesito un EmailService"
Spring dice: "¡No hay problema, yo te lo doy!"
Spring crea EmailService y se lo da a OrderService
El control de la creación está INVERTIDO: ¡Spring lo gestiona, no usted!
✅ Enfoque con IoC (Inyección de Dependencias)
Código Mejorado
@Service
public class OrderService {
// NO se crea el objeto
private final EmailService emailService;
// Se SOLICITA el objeto vía el constructor
public OrderService(EmailService emailService) {
this.emailService = emailService;
}
public void processOrder(Order order) {
saveOrder(order);
emailService.sendEmail(order.getCustomerEmail(), "Commande confirmée");
}
}
Lo Que Sucede
Ventajas de IoC
1. Bajo Acoplamiento
// OrderService NO conoce la implementación concreta
private final EmailService emailService;
// ^^^^^^^^^^^^
// Interfaz (abstracción)
Puede tener varias implementaciones:
public interface EmailService {
void sendEmail(String to, String message);
}
public class GmailEmailService implements EmailService {
// Envía vía Gmail
}
public class SendGridEmailService implements EmailService {
// Envía vía SendGrid
}
¡Spring elige cuál usar según la configuración!
2. Fácil de Modificar
Cambiar la implementación SIN tocar OrderService:
// Configuración Spring
@Configuration
public class AppConfig {
@Bean
public EmailService emailService() {
// Antes
// return new GmailEmailService();
// Después - ¡Cambio en UN solo lugar!
return new SendGridEmailService();
}
}
¡OrderService NO necesita cambiar!
3. Fácil de Probar
@Test
public void testProcessOrder() {
// Se crea un "falso" EmailService para las pruebas
EmailService fakeEmailService = new EmailService() {
@Override
public void sendEmail(String to, String message) {
System.out.println("Correo simulado enviado a: " + to);
// ¡No se envía un correo real!
}
};
// Se inyecta el servicio falso
OrderService service = new OrderService(fakeEmailService);
Order order = new Order(...);
service.processOrder(order);
// ✅ ¡No se envía un correo real!
}
El Contenedor Spring (ApplicationContext)
¿Qué es el Contenedor?
El Contenedor Spring = El gestor de objetos de Spring
Sus responsabilidades:
- ✅ Crear los objetos (beans)
- ✅ Configurarlos
- ✅ Ensamblarlos (inyectar las dependencias)
- ✅ Gestionar su ciclo de vida
Analogía
Piense en el Contenedor Spring como un director de orquesta:
Director de Orquesta (Contenedor Spring)
↓
Músicos (Beans: Controller, Service, Repository)
↓
La orquesta toca música (La aplicación funciona)
El director de orquesta:
- ✅ Recluta a los músicos (crea los beans)
- ✅ Los coloca en el lugar correcto (inyección)
- ✅ Coordina su trabajo (gestión del ciclo de vida)
El Contenedor en Acción
// Al iniciar la aplicación
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// Esta línea inicia el Contenedor Spring
SpringApplication.run(DemoApplication.class, args);
// ^^^
// ¡El Contenedor inicia aquí!
}
}
Lo que hace el Contenedor al iniciar:
1. Escanear las clases con @Component, @Service, @Repository, @Controller
↓
2. Crear las instancias (beans)
↓
3. Resolver las dependencias
↓
4. Inyectar las dependencias
↓
5. ¡Aplicación lista!
Visualización del Contenedor
¡El Contenedor gestiona TODOS los objetos y sus dependencias!
Ejemplo Completo
Sin IoC (Código Antiguo)
public class OrderService {
private EmailService emailService = new EmailService();
private PaymentService paymentService = new PaymentService();
private InventoryService inventoryService = new InventoryService();
public void processOrder(Order order) {
paymentService.charge(order);
inventoryService.reserve(order);
emailService.sendConfirmation(order);
}
}
Problemas:
- ❌ 3 dependencias creadas con
new - ❌ Acoplamiento fuerte en todas partes
- ❌ Imposible de probar
- ❌ Rígido y frágil
Con IoC (Código Spring)
@Service
public class OrderService {
// Declaración de dependencias (sin 'new')
private final EmailService emailService;
private final PaymentService paymentService;
private final InventoryService inventoryService;
// Inyección vía el constructor
public OrderService(
EmailService emailService,
PaymentService paymentService,
InventoryService inventoryService
) {
this.emailService = emailService;
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}
public void processOrder(Order order) {
paymentService.charge(order);
inventoryService.reserve(order);
emailService.sendConfirmation(order);
}
}
Ventajas:
- ✅ 0 uso de
new(Spring gestiona todo) - ✅ Bajo acoplamiento
- ✅ Fácil de probar (se pueden inyectar fakes)
- ✅ Flexible y mantenible
Conceptos Clave para Recordar
1. Inversión de Control (IoC)
"Spring controla la creación de los objetos, no usted."
Antes: Usted crea sus objetos con new
Después: Spring crea sus objetos y se los da
2. Inyección de Dependencias (DI)
"Spring inyecta (da) las dependencias que usted necesita."
Mecanismo: Vía el constructor, setters, o directamente en los campos
3. Contenedor Spring
"El gestor central que crea y ensambla todos los objetos."
Responsabilidades:
- Crear los beans
- Resolver las dependencias
- Inyectar las dependencias
- Gestionar el ciclo de vida
Resumen del Capítulo
El Problema
// ❌ Acoplamiento fuerte
private EmailService service = new EmailService();
Consecuencias:
- Difícil de modificar
- Difícil de probar
- Código rígido
La Solución
// ✅ IoC + Inyección de Dependencias
@Service
public class OrderService {
private final EmailService service;
public OrderService(EmailService service) {
this.service = service;
}
}
Ventajas:
- Bajo acoplamiento
- Fácil de modificar
- Fácil de probar
- Código flexible
Analogía Final
IoC = Restaurante
Sin IoC:
Usted: "Tengo hambre"
Usted: "Voy a comprar los ingredientes"
Usted: "Voy a cocinar"
Usted: "Como"
Con IoC:
Usted: "Tengo hambre" (declaración de la necesidad)
Restaurante: "¡Aquí está su plato!" (Spring provee)
Usted: "Como" (usted utiliza)
Usted no crea el plato, se lo DAN. ¡Eso es IoC!
Autoevaluación
Responda estas preguntas para verificar su comprensión:
- ¿Qué es la Inversión de Control?
- ¿Cuál es el problema del acoplamiento fuerte?
- ¿Cuál es el papel del Contenedor Spring?
- ¿Por qué IoC facilita las pruebas?
Si no puede responder, ¡repase el capítulo!
Próximo Paso
Ahora que comprende POR QUÉ existe IoC, pasemos al CÓMO: la Inyección de Dependencias.