Einleitung: Was ist Dependency Injection und warum wird es in Java benötigt
In der modernen Java-Entwicklung sind Code-Sauberkeit und Testbarkeit einige der Schlüsselanforderungen an einen Profi. Dependency Injection (DI) ist ein Entwurfsmuster, das Ihren Code lose gekoppelt, flexibel und leicht wartbar macht. Anstatt dass ein Objekt seine Abhängigkeiten selbst erstellt (z. B. über new), „injiziert“ DI sie von außen. Dies ist ein grundlegender Bestandteil des Prinzips der Inversion of Control (IoC).
In diesem Artikel werden wir untersuchen, wie Dependency Injection in Java funktioniert, drei Hauptmethoden seiner Implementierung betrachten (über Konstruktor, Setter und Interface) und zeigen, wie DI in realen Projekten angewendet wird, einschließlich des beliebten Frameworks Spring.
Warum wird Ihr Code ohne DI zerbrechlich?
Stellen Sie sich eine Klasse Car vor, die in sich selbst einen Motor erstellt:
public class Car { private Engine engine;
public Car() { this.engine = new PetrolEngine(); // Feste Kopplung }
public void start() { engine.run(); }}Die Probleme dieses Ansatzes sind offensichtlich:
- Keine Wiederverwendbarkeit: Sie können
PetrolEnginenicht einfach durchElectricEngineersetzen. - Schwierigkeit beim Testen: Für Unit-Tests müssten Sie einen echten Motor erstellen, anstatt einen Mock zu verwenden.
- Verletzung des SRP: Die Klasse
Carkümmert sich nicht nur um die Steuerung des Autos, sondern auch um die Erstellung des Motors.
Dependency Injection löst diese Probleme, indem es die Erstellung von Abhängigkeiten an externen Code (Container oder andere Klasse) delegiert.
Drei Arten der Abhängigkeitsinjektion in Java
In Java gibt es drei Hauptansätze zur Implementierung von DI. Betrachten wir jeden am Beispiel eines Benachrichtigungssystems.
1. Injektion über den Konstruktor (Constructor Injection)
Dies ist die bevorzugte Methode. Abhängigkeiten werden über Konstruktorparameter übergeben. Dies stellt sicher, dass das Objekt immer in einem gültigen Zustand erstellt wird und Abhängigkeiten nach der Erstellung nicht geändert werden können (Unveränderlichkeit).
public interface MessageService { void sendMessage(String message, String recipient);}
public class EmailService implements MessageService { @Override public void sendMessage(String message, String recipient) { System.out.println("Email sent to " + recipient + ": " + message); }}
public class NotificationSender { private final MessageService messageService;
// Abhängigkeitsinjektion über den Konstruktor public NotificationSender(MessageService messageService) { this.messageService = messageService; }
public void notifyUser(String userEmail, String msg) { messageService.sendMessage(msg, userEmail); }}
// Verwendungpublic 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. Injektion über Setter (Setter Injection)
Geeignet für optionale Abhängigkeiten oder wenn die Abhängigkeit während der Lebensdauer des Objekts geändert werden kann. Allerdings kann das Objekt in einem unvollständigen Zustand erstellt werden, wenn der Setter vergessen wird aufzurufen.
public class NotificationSender { private MessageService messageService;
// Setter für die Abhängigkeitsinjektion public void setMessageService(MessageService messageService) { this.messageService = messageService; }
public void notifyUser(String userEmail, String msg) { if (messageService != null) { messageService.sendMessage(msg, userEmail); } else { System.o