Saltar al contenido principal

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​

  1. El problema del acoplamiento fuerte (y por qué es malo)
  2. La solución: la Inversión de Control
  3. Cómo Spring implementa IoC
  4. 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:

  • OrderService DEPENDE DIRECTAMENTE de EmailService
  • 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:

  1. ✅ Crear los objetos (beans)
  2. ✅ Configurarlos
  3. ✅ Ensamblarlos (inyectar las dependencias)
  4. ✅ 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:

  1. ¿Qué es la Inversión de Control?
  2. ¿Cuál es el problema del acoplamiento fuerte?
  3. ¿Cuál es el papel del Contenedor Spring?
  4. ¿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.

➡️ Capítulo 2: Inyección de Dependencias

⬅️ Volver al Módulo 5