Inyección de Dependencias (Dependency Injection) en Java: guía completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué Dependency Injection es imprescindible para el desarrollador Java

Cada desarrollador Java, tarde o temprano, se enfrenta al problema del acoplamiento rígido del código. Cuando una clase crea directamente instancias de otras clases, el sistema se vuelve frágil, difícil de probar y modificar. La solución a este problema es Dependency Injection (DI), o inyección de dependencias. Es uno de los principios clave del desarrollo moderno, que subyace en frameworks como Spring y Jakarta EE.

En este artículo, analizaremos qué es DI, en qué se diferencia de IoC (Inversion of Control), veremos las tres formas principales de inyectar dependencias en Java con ejemplos de código claros, y discutiremos por qué este patrón es necesario para crear aplicaciones mantenibles.



¿Qué son Dependency Injection e Inversion of Control?

Antes de pasar al código, es importante entender dos conceptos fundamentales.

Inversion of Control (IoC) es un principio general en el que el flujo de control del programa se transfiere a un framework o contenedor externo. En lugar de que el código decida por sí mismo cuándo crear objetos y llamar a métodos, un sistema externo se encarga de ello.

Dependency Injection es una implementación concreta del principio IoC. Es un patrón en el que un objeto recibe sus dependencias (otros objetos de los que depende) desde el exterior, en lugar de crearlos él mismo.

En términos simples, en lugar de que la clase Car cree el motor por sí misma (new Engine()), recibe un motor ya listo a través del constructor o un setter. Esto hace que la clase Car sea independiente de la implementación concreta de Engine.



Tres formas de inyectar dependencias en Java

En Java existen tres formas principales de implementar DI. Veamos cada una con el ejemplo de un sistema simple de notificaciones.



1. Inyección por constructor (Constructor Injection)

Esta es la forma más recomendada y segura. Las dependencias se pasan a través del constructor de la clase. Esto garantiza que el objeto nunca se cree en un estado inválido, ya que todas las dependencias necesarias son obligatorias.

// Interfaz para el servicio de notificacionespublic interface MessageService {    void sendMessage(String message, String recipient);}

// Implementación para Emailpublic class EmailService implements MessageService { @Override public void sendMessage(String message, String recipient) { System.out.println("Email sent to " + recipient + ": " + message); }}

// Clase que utiliza el servicio de notificacionespublic class NotificationSender { private final MessageService service; // Dependencia

// Inyección por constructor public NotificationSender(MessageService service) { this.service = service; }

public void notifyUser(String user, String msg) { this.service.sendMessage(msg, user); }}

// Usopublic class Main { public static void main(String[] args) { MessageService emailService = new EmailService(); NotificationSender sender = new NotificationSender(emailService); // DI sender.notifyUser("user@example.com", "Hello!"); }}


2. Inyección por setter (Setter Injection)

Esta forma se utiliza cuando la dependencia no es obligatoria o puede cambiarse después de crear el objeto. La dependencia se pasa a través de un método setter público.

public class NotificationSenderWithSetter {    private MessageService service; // Dependencia no obligatoria

// Setter para la inyección public void setMessageService(MessageService service) { this.service = service; }

public void notifyUser(String user, String msg) { if (service != null) { this.service.sendMessage(msg, user); } else { System.out.println("No service configured!"); } }}

// Uso

Blogs

Book Recommendations