Dependency Injection mit Spring
Datum: Juni 2021
Lesedauer: 3 Minuten
Theorie
Einen wiederverwendbaren, tastbaren und lesbaren Code. All das bringt die Dependency Injection (DI) mit sich.
Mithilfe der Dependency Injection ist es möglich, dass ein Objekt andere benötigte Objekte (Dependencies) erhält. Die Kompetenten werden auf einem zentralen Container verwaltet und nicht direkt mit anderen Komponenten gekoppelt.
Beispiel
Ohne DI
Die Komponente A
erzeugt ein Objekt. Dieses wird von Komponente D
benötigt. Nun muss das Objekt bis zur Klasse D
geschoben werden. Dies wird "dependency carrying" genannt.
A erzeugt Objekt
A B C D Objekt
Mit DI
Durch das Einsetzen der DI werden die Klassen lose gekoppelt. D
kann das Objekt verwenden, ohne es vorerst zu "injecten". Es ist bereits bekannt.
Container erzeugt Objekt
Container erzeugt D und injiziert Objekt
Container erzeugt C und injiziert D
Container erzeugt B und injiziert C
Container erzeugt A und injiziert B
A B C D Objekt
Vorteile
Weniger Abhängigkeiten
Bei einem Objekt können die Abhängigkeiten schnell ändern. Geschieht dies, so muss man unter Umständen Änderungen an einer Komponente vornehmen.
Beispiel: Die Parameter einer Methode verändern sich. Nun muss man den Aufruf der Methode anpassen. Ist eine Komponente nicht sehr abhängig von einer Methode, so fallen weniger Änderungen an.
Besserer Code
Das Verwenden der DI machten den Code automatisch sauberer, da in der Schnittstelle ersichtlich ist, welche Abhängigkeiten eine Komponente hat. Da die Abhängigkeiten extern konfiguriert werden, sind die Komponenten auch wiederverwendbarer.
Testbarer
Ein weiter positiver Aspekt ist die Testbarkeit. Beim Testen ist die Klasse nicht auf die Funktionsfähigkeit einer anderen Klasse angewiesen, da man zuvor eine Mock-Implementation injecten kann. Bei diesem Mock Objekt kann man das Verhalten konfigurieren. Somit ist es möglich, nur eine einzelne, gekapselte Klasse zu testen.
Container
Ich habe jetzt oft von einem Container geschrieben. Doch was ist dies eigentlich?
Der Spring IoC Container ist ein Framework, welches für das Instanziieren und Konfigurieren von Beans zuständig ist. Dies geschieht automatisch. Jedoch kann man auch manuell einen solchen Container erstellen.
ApplicationContext context = new ClassPathXmlApplicationContext("appContext.xml");
Implementierung
Es gibt verschiedene Varianten, die DI zu nutzen.
Um die Beispiele nachvollziehen zu können, muss man die Annotationen erkennen können.
Annotation | Beschreibung |
---|---|
@Configuration | Signalisiert dem Container, dass die Klasse Beans enthält |
@Bean | Definiert, dass es sich um eine Bean handelt |
Kontruktor
Die Klasse muss kein neues Objekt erstellen. Das Team muss lediglich den Player injecten.
public class Team {
private Player player;
public Team(Player player) {
this.player = player;
}
}
Konfiguration
Alle benötigten Beans landen in einem Konfigurationsfile, solange sie dort noch nicht existieren.
@Configuration
public class AppConfig {
@Bean
public Player player1() {
return new PlayerImpl1();
}
@Bean
public Team team() {
return new Team(player1());
}
}
Es spielt theoretisch keine Rolle, wo man diese schreibt. Jedoch sollte man auf die Sauberkeit des Projekts achten und deshalb alle Beans an einem sinnvollen Ort definieren.
Setter
Diese Variante wird meistens genutzt, um optionale Klassen zu definieren. Denn eine Verknüpfung muss so nicht zwingend hergestellt werden.
@Bean
public Team team() {
Team team = new Team();
team.setPlayer(player1());
return team;
}
Interface
Über ein Interface wird sichergestellt, dass die Komponente injiziert wird.
public interface Team {
void injiziere(final Coach coach);
}
public class Basel implements Team {
private final Coach coach;
public void injiziere(final Coach coach) {
this.player = player;
}
}
public class Team {
public void methode() {
public final Coach coach = ... ;
public final Team team = ... ;
team.injiziere(player);
}
}
Autowired
Autowired sieht sehr sauber aus und ist deshalb auch mein Favorit. Man muss lediglich mit @Autowired
das gewünschte Objekt angeben.
public class Team {
@Autowired
private Player player;
}
Fazit
Natürlich gibt es auch Nachteile. Versteht man das Prinzip der DI nicht, ist es schwierig nachzuvollziehen, was im Code geschieht. Auch die Annotationen
muss man zuerst kennen.
Es ist auch nicht von Vorteil, wenn man von einem Framework abhängig ist.
Zudem kann es sein, dass durch die DI, die Automatisierung der IDE beeinträchtigt wird. Es kann also vorkommen, dass z.B. Visual Studio Code keine
Vorschau auf eine Referenz anzeigen kann.
Jedoch überwiegen, meiner Meinung nach, die Vorteile.