2021
DI mit Spring

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.

AnnotationBeschreibung
@ConfigurationSignalisiert dem Container, dass die Klasse Beans enthält
@BeanDefiniert, 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.