15. Spring IoC
15.1. Einführung
Unser Ziel ist es, die Konfigurations- und Integrationsmöglichkeiten des Spring-Frameworks (http://www.springframework.org) zu untersuchen sowie das Konzept von IoC (Inversion of Control), auch bekannt als Dependency Injection, zu definieren und anzuwenden
Betrachten wir die soeben erstellte 3-Tier-Anwendung:
![]() |
Um auf Benutzeranfragen zu reagieren, muss der [Application]-Controller mit der [service]-Schicht kommunizieren. In unserem Beispiel handelte es sich dabei um eine Instanz vom Typ [DaoImpl]. Der [Application]-Controller hat in seiner [init]-Methode (Abschnitt 14.8.3) eine Referenz auf die [service]-Schicht abgerufen:
- Zeile 6: Die [dao]-Schicht wurde durch die explizite Erstellung einer [DaoImpl]-Instanz instanziiert
- Zeile 9: Die [service]-Schicht wurde durch die explizite Erstellung einer Instanz von [ServiceImpl] instanziiert
Erinnern Sie sich daran, dass die Klassen [DaoImpl] und [ServiceImpl] Schnittstellen implementieren, nämlich die Schnittstellen [IDao] bzw. [IService]. In zukünftigen Versionen wird die Schnittstelle [IDao] durch eine Klasse implementiert, die eine Liste von Personen verwaltet, die in einer Datenbank gespeichert sind. Nennen wir diese Klasse für dieses Beispiel [DaoBD]. Das Ersetzen der [DaoImpl]-Implementierung der [dao]-Schicht durch die [DaoBD]-Implementierung erfordert eine Neukompilierung der [web]-Schicht. Tatsächlich muss Zeile 6 oben, die die [dao]-Schicht mit einem [DaoImpl]-Typ instanziiert, diese nun mit einem [DaoBD]-Typ instanziieren. Unsere [web]-Schicht ist daher von der [dao]-Schicht abhängig. Zeile 9 oben zeigt, dass sie auch von der [service]-Schicht abhängig ist.
Spring IoC ermöglicht es uns, eine 3-Schichten-Anwendung zu erstellen, bei der die Schichten voneinander unabhängig sind, d. h., die Änderung einer Schicht erfordert keine Änderung der anderen. Dies bietet große Flexibilität bei der Weiterentwicklung der Anwendung.
Die bisherige Architektur wird sich wie folgt weiterentwickeln:
![]() |
Mit [Spring IoC] bezieht der [Application]-Controller die benötigte Referenz wie folgt aus der [Service]-Schicht:
- In seiner [init]-Methode fordert er die [Spring IoC]-Schicht auf, eine Referenz auf die [Service]-Schicht bereitzustellen
- [Spring IoC] verwendet dann eine XML-Konfigurationsdatei, die angibt, welche Klasse instanziiert und wie sie initialisiert werden soll.
- [Spring IoC] gibt die Referenz auf die erstellte [Service]-Schicht an den [Application]-Controller zurück.
Der Vorteil dieser Lösung besteht darin, dass die Namen der Klassen, die die verschiedenen Schichten instanziieren, nicht mehr fest in der [init]-Methode des Controllers codiert sind, sondern lediglich in einer Konfigurationsdatei aufgeführt werden. Eine Änderung der Implementierung einer Schicht erfordert eine Änderung in dieser Konfigurationsdatei, nicht jedoch im Controller.
Lassen Sie uns nun die Möglichkeiten von [Spring IoC] anhand von Beispielen erkunden.
15.2. Spring IoC in der Praxis
15.2.1. Spring
[Spring IoC] ist Teil eines größeren Projekts, das unter [http://www.springframework.org/] (Mai 2006) verfügbar ist:
![]() | ![]() |
![]() |
- [1]: Spring nutzt verschiedene Technologien von Drittanbietern, die hier als Abhängigkeiten bezeichnet werden. Sie müssen die Version mit den Abhängigkeiten herunterladen, um zu vermeiden, dass Sie die Tool-Bibliotheken von Drittanbietern später separat herunterladen müssen.
- [2]: Die Verzeichnisstruktur der heruntergeladenen ZIP-Datei
- [3]: Die [Spring]-Distribution, d. h. die .jar-Archive des Spring-Projekts selbst ohne dessen Abhängigkeiten. Der [IoC]-Aspekt von Spring wird durch die Archive [spring-core.jar, spring-beans.jar] bereitgestellt.
- [4,5]: die Tool-Archive von Drittanbietern
15.2.2. Eclipse-Projekte für die Beispiele
Wir werden drei Beispiele erstellen, die die Verwendung von Spring IoC veranschaulichen. Sie werden alle im folgenden Eclipse-Projekt enthalten sein:

Das Projekt [springioc-examples] ist so konfiguriert, dass sich die Quelldateien und kompilierten Klassen im Stammverzeichnis des Projektordners befinden:
![]() |
- [1]: Die Projektordnerstruktur in [Eclipse]
- [2]: Die Spring-Konfigurationsdateien befinden sich im Stammverzeichnis des Projekts und somit im Klassenpfad der Anwendung
- [3]: die Klassen aus Beispiel 1
- [4]: die Klassen aus Beispiel 2
- [5]: die Klassen aus Beispiel 3
- [6]: Die Projektbibliotheken [spring-core.jar, spring-beans.jar] befinden sich im Ordner [dist] der Spring-Distribution und [commons-logging.jar] im Ordner [lib/jakarta-commons]. Diese drei Archive wurden in den Klassenpfad der Anwendung aufgenommen.
15.2.3. Beispiel 1
Die Elemente aus Beispiel 1 wurden im Paket [springioc01] des Projekts abgelegt:
![]()
Die Klasse [Person] sieht wie folgt aus:
Die Klasse enthält:
- Zeilen 6–7: zwei private Felder, name und age
- Zeilen 23–38: die get- und set-Methoden für diese beiden Felder
- Zeilen 10–12: eine toString-Methode, um den Wert des [Person]-Objekts als Zeichenkette abzurufen
- Zeilen 15–21: eine init-Methode, die von Spring beim Erstellen des Objekts aufgerufen wird, und eine close-Methode, die beim Löschen des Objekts aufgerufen wird
Um Objekte vom Typ [Person] mit Spring zu instanziieren, verwenden wir die folgende Datei [spring-config-01.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc01.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
</beans>
- Zeilen 3, 12: Das <beans>-Tag ist das Stamm-Tag von Spring-Konfigurationsdateien. Innerhalb dieses Tags wird das <bean>-Tag verwendet, um die verschiedenen zu erstellenden Objekte zu definieren.
- Zeilen 4–7: Definition eines Beans
- Zeile 4: Die Bean heißt [person1] (id-Attribut) und ist eine Instanz der Klasse [istia.st.springioc01.Person] (class-Attribut). Die [init]-Methode der Instanz wird nach ihrer Erstellung aufgerufen (init-method-Attribut), und die [close]-Methode der Instanz wird vor ihrer Löschung aufgerufen (destroy-method-Attribut).
- Zeile 5: Definiert den Wert, der der Eigenschaft [name] (Attribut „name“) der erstellten Instanz [Person] zugewiesen werden soll. Um diese Initialisierung durchzuführen, verwendet Spring die Methode [setName]. Diese Methode muss daher vorhanden sein. Dies ist hier der Fall.
- Zeile 6: Wie oben für die Eigenschaft [age].
- Zeilen 8–11: Ähnliche Definition eines Beans namens [person2]
Die Testklasse [Main] sieht wie folgt aus:
Kommentare:
- Zeile 10: Um die in der Datei [spring-config-01.xml] definierten Beans abzurufen, verwenden wir ein [XmlBeanFactory]-Objekt, mit dem wir die in einer XML-Datei definierten Beans instanziieren können. Die Datei [spring-config-01.xml] wird im [ClassPath] der Anwendung abgelegt, d. h. in einem der Verzeichnisse, die von der Java Virtual Machine durchsucht werden, wenn sie nach einer von der Anwendung referenzierten Klasse sucht. Das [ClassPathResource]-Objekt wird verwendet, um im [ClassPath] einer Anwendung nach einer Ressource zu suchen, in diesem Fall nach der Datei [spring-config-01.xml]. Das resultierende [bf]-Objekt (Bean Factory) ermöglicht es uns, mit der Anweisung bf.getBean("XX") eine Referenz auf eine Bean namens „XX“ zu erhalten.
- Zeile 12: Es wird eine Referenz für die Bean mit dem Namen [person1] in der Datei [spring-config-01.xml] angefordert.
- Zeile 13: Der Wert des entsprechenden [Person]-Objekts wird angezeigt.
- Zeilen 15–16: Wir verfahren ebenso für die Bean namens [person2].
- Zeilen 18–19: Wir fordern die Bean mit dem Namen [person2] erneut an.
- Zeile 21: Alle Beans in [bf] werden entfernt, d. h. diejenigen, die aus der Datei [spring-config-01.xml] erstellt wurden.
Die Ausführung der Klasse [Main] liefert folgende Ergebnisse:
Kommentare:
- Zeile 1 wurde durch Ausführen von Zeile 12 von [Main] erhalten. Die Operation
erzwang die Erstellung des Beans [person1]. Da in der Definition des Beans [person1] [init-method="init"] angegeben war, wurde die Methode [init] des erstellten [Person]-Objekts ausgeführt. Die entsprechende Meldung wird angezeigt.
- Zeile 2: In Zeile 13 von [Main] wurde der Wert des erstellten [Person]-Objekts angezeigt.
- Zeilen 3–4: Der gleiche Vorgang wiederholt sich für den Bean namens [person2].
- Zeile 5: Der Vorgang in den Zeilen 18–19 von [Main]
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
hat nicht zur Erstellung eines neuen Objekts vom Typ [Person] geführt. Wäre dies der Fall gewesen, wäre die Methode [init] angezeigt worden. Dies ist das Singleton-Prinzip. Standardmäßig erstellt Spring nur eine einzige Instanz der Beans in seiner Konfigurationsdatei. Es handelt sich um einen Objektreferenzdienst. Wird eine Referenz auf ein Objekt angefordert, das noch nicht erstellt wurde, erstellt er es und gibt eine Referenz zurück. Ist das Objekt bereits erstellt, gibt Spring einfach eine Referenz darauf zurück. Da [person2] hier bereits erstellt wurde, gibt Spring einfach eine Referenz darauf zurück.
- Die Ausgabe in den Zeilen 6–7 wurde durch Zeile 21 von [Main] ausgelöst, die die Zerstörung aller Beans anfordert, auf die das Objekt [XmlBeanFactory bf] verweist, nämlich die Beans [person1] und [person2]. Da diese beiden Beans das Attribut [destroy-method="close"] haben, wird die Methode [close] beider Beans ausgeführt, wodurch die Zeilen 6–7 angezeigt werden.
Nachdem wir nun die Grundlagen einer Spring-Konfiguration behandelt haben, können wir unsere Erläuterungen etwas schneller durchgehen.
15.2.4. Beispiel 2
Die Elemente von Beispiel 2 befinden sich im Paket [springioc02] des Projekts:

Das Paket [springioc02] wird zunächst durch Kopieren und Einfügen des Pakets [springioc01] erstellt, dann wird die Klasse [Car] hinzugefügt und die Klasse [Main] an das neue Beispiel angepasst.
Die Klasse [Car] sieht wie folgt aus:
Die Klasse enthält:
- Zeilen 5–7: drei private Felder: type, make und owner. Diese Felder können mithilfe der öffentlichen get- und set-Methoden in den Zeilen 26–48 initialisiert und ausgelesen werden. Sie können auch mithilfe des in den Zeilen 13–17 definierten Konstruktors Car(String, String, Person) initialisiert werden. Die Klasse verfügt außerdem über einen Konstruktor ohne Argumente, um dem JavaBean-Standard zu entsprechen.
- Zeilen 20–23: eine toString-Methode, um den Wert des [Car]-Objekts als String abzurufen
- Zeilen 51–57: eine `init`-Methode, die von Spring unmittelbar nach der Erstellung des Objekts aufgerufen wird, und eine `close`-Methode, die beim Löschen des Objekts aufgerufen wird
Um Objekte vom Typ [Car] zu erstellen, verwenden wir die folgende Spring-Datei [spring-config-02.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc02.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
<bean id="voiture1" class="istia.st.springioc02.Voiture" init-method="init" destroy-method="close">
<constructor-arg index="0" value="Peugeot" />
<constructor-arg index="1" value="307" />
<constructor-arg index="2">
<ref local="personne2" />
</constructor-arg>
</bean>
</beans>
Diese Datei fügt den in [spring-config-01.xml] definierten Beans (Zeilen 12–17) eine Bean mit dem Schlüssel „car1“ vom Typ [Car] hinzu. Um diese Bean zu initialisieren, hätten wir schreiben können:
Anstatt die bereits in Beispiel 1 vorgestellte Methode zu wählen, haben wir uns hier für den Konstruktor Car(String, String, Person) der Klasse entschieden.
- Zeile 12: Definition des Namens des Beans, seiner Klasse, der nach seiner Instanziierung auszuführenden Methode und der nach seiner Zerstörung auszuführenden Methode.
- Zeile 13: Wert des ersten Parameters des Konstruktors [Car(String, String, Person)].
- Zeile 14: Wert des zweiten Parameters des Konstruktors [Car(String, String, Person)].
- Zeilen 15–17: Wert des dritten Parameters des Konstruktors [Car(String, String, Person)]. Dieser Parameter ist vom Typ [Person]. Wir übergeben ihm die Referenz (ref-Tag) der Bean [person2], die in derselben Datei definiert ist (lokales Attribut).
Für unsere Tests verwenden wir die folgende [Main]-Klasse:
Die Methode [main] ruft die Referenz auf die Bean [car1] ab (Zeile 12) und gibt sie aus (Zeile 13). Die Ergebnisse lauten wie folgt:
Kommentare:
- Die [main]-Methode fordert eine Referenz auf die [car1]-Bean an (Zeile 12). Spring beginnt mit der Erstellung der [car1]-Bean, da diese Bean noch nicht erstellt wurde (Singleton). Da die [car1]-Bean auf die [person2]-Bean verweist, wird diese wiederum erstellt. Die [person2]-Bean wurde erstellt. Anschließend wird ihre [init]-Methode ausgeführt (Zeile 1 der Ergebnisse). Dann wird die [car1]-Bean instanziiert. Anschließend wird ihre [init]-Methode ausgeführt (Zeile 2 der Ergebnisse).
- Zeile 3 der Ergebnisse stammt aus Zeile 13 von [main]: Der Wert der Bean [car1] wird angezeigt.
- Zeile 15 von [main] fordert die Zerstörung aller vorhandenen Beans an, was dazu führt, dass die Zeilen 4 und 5 der Ergebnisse angezeigt werden.
15.2.5. Beispiel 3
Die Elemente von Beispiel 3 befinden sich im Paket [springioc03] des Projekts:

Das Paket [springioc03] wird zunächst durch Kopieren und Einfügen des Pakets [springioc01] erstellt, dann wird die Klasse [GroupePersonnes] hinzugefügt, die Klasse [Voiture] entfernt und die Klasse [Main] an das neue Beispiel angepasst.
Die Klasse [GroupePersonnes] sieht wie folgt aus:
Seine beiden privaten Mitglieder sind:
Zeile 8: members: ein Array von Personen, die Mitglieder der Gruppe sind
Zeile 9: workingGroups: ein Wörterbuch, das eine Person einer Arbeitsgruppe zuordnet
Beachten Sie hier, dass die Klasse [PersonGroup] keinen Konstruktor ohne Argumente definiert. Erinnern Sie sich daran, dass es bei Fehlen eines Konstruktors einen „Standard“-Konstruktor gibt, bei dem es sich um den Konstruktor ohne Argumente handelt, der nichts tut.
Das Ziel hier ist es, zu zeigen, wie Spring es Ihnen ermöglicht, komplexe Objekte zu initialisieren, beispielsweise solche mit Array- oder Dictionary-Feldern. Die Bean-Datei [spring-config-03.xml] für Beispiel 3 lautet wie folgt:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="personne1" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Simon" />
<property name="age" value="40" />
</bean>
<bean id="personne2" class="istia.st.springioc03.Personne" init-method="init" destroy-method="close">
<property name="nom" value="Brigitte" />
<property name="age" value="20" />
</bean>
<bean id="groupe1" class="istia.st.springioc03.GroupePersonnes" init-method="init" destroy-method="close">
<property name="membres">
<list>
<ref local="personne1" />
<ref local="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<map>
<entry key="Brigitte" value="Marketing" />
<entry key="Simon" value="Ressources humaines" />
</map>
</property>
</bean>
</beans>
- Zeilen 14–17: Mit dem <list>-Tag können Sie ein Feld vom Typ Array oder ein Feld, das die List-Schnittstelle implementiert, mit verschiedenen Werten initialisieren.
- Zeilen 20–23: Mit dem <map>-Tag können Sie dasselbe mit einem Feld tun, das die Map-Schnittstelle implementiert.
Für unsere Tests verwenden wir die folgende [Main]-Klasse:
- Zeilen 12–13: Wir fordern von Spring eine Referenz auf die Bean [group1] an und zeigen deren Wert an.
Die Ergebnisse lauten wie folgt:
Kommentare:
- In Zeile 12 von [Main] wird eine Referenz auf die Bean [group1] angefordert. Spring beginnt mit der Erstellung dieser Bean. Da die Bean [group1] auf die Beans [person1] und [person2] verweist, werden diese beiden Beans erstellt (Zeilen 1 und 2 der Ergebnisse). Anschließend wird die Bean [group1] instanziiert und ihre Methode [init] ausgeführt (Zeile 3 der Ergebnisse).
- Zeile 13 von [Main] zeigt Zeile 4 der Ergebnisse an.
- Zeile 15 von [Main] zeigt die Zeilen 5–7 der Ergebnisse an.
15.3. Konfigurieren einer n-Tier-Anwendung mit Spring
Betrachten Sie eine 3-Tier-Anwendung mit folgender Struktur:
UserDataBusinessLayer [business]DataAccessLayer [dao]UserInterfaceLayer [ui]
Hier möchten wir die Vorteile der Verwendung von Spring beim Aufbau einer solchen Architektur aufzeigen.
- Die drei Schichten werden durch die Verwendung von Java-Schnittstellen voneinander unabhängig gemacht
- Die Integration der drei Schichten wird von Spring übernommen
Die Anwendungsstruktur in Eclipse könnte wie folgt aussehen:
![]() |
- [1]: die [DAO]-Schicht:
- [IDao]: die Schnittstelle der Schicht
- [Dao1, Dao2]: zwei Implementierungen dieser Schnittstelle
- [2]: die [Business]-Schicht:
- [IMetier]: die Schnittstelle der Schicht
- [Business1, Business2]: zwei Implementierungen dieser Schnittstelle
- [3]: die [UI]-Schicht:
- [IUi]: die Schnittstelle der Schicht
- [Ui1, Ui2]: zwei Implementierungen dieser Schnittstelle
- [4]: die Spring-Konfigurationsdateien der Anwendung. Wir werden die Anwendung auf zwei Arten konfigurieren.
- [5]: die von der Anwendung benötigten Bibliotheken. Dies sind die in den vorherigen Beispielen verwendeten.
- [6]: das Testpaket. [Main1] verwendet die Konfiguration [spring-config-01.xml] und [Main2] die Konfiguration [spring-config-02.xml].
Der Zweck dieses Beispiels ist es zu zeigen, dass wir die Implementierung einer oder mehrerer Schichten der Anwendung ändern können, ohne dass dies Auswirkungen auf die anderen Schichten hat. Alles geschieht in der Spring-Konfigurationsdatei.
Die [dao]-Schicht
Die [dao]-Schicht implementiert die folgende [IDao]-Schnittstelle:
Die Implementierung [Dao1] sieht wie folgt aus:
Die Implementierung [Dao2] sieht wie folgt aus:
Die [Business]-Schicht
Die [Business]-Schicht implementiert die folgende [IMetier]-Schnittstelle:
Die Implementierung von [Business1] sieht wie folgt aus:
Die [Metier2]-Implementierung sieht wie folgt aus:
Die [ui]-Ebene
Die [ui]-Schicht implementiert die folgende [IUi]-Schnittstelle:
Die Implementierung [Ui1] sieht wie folgt aus:
Die Implementierung [Ui2] sieht wie folgt aus:
Spring-Konfigurationsdateien
Die erste [spring-config-01.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- the dao class -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao1"/>
<!-- the business class -->
<bean id="metier" class="istia.st.springioc.troistier.metier.Metier1">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- the UI class -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui1">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
Die zweite [spring-config-02.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- the dao class -->
<bean id="dao" class="istia.st.springioc.troistier.dao.Dao2"/>
<!-- the business class -->
<bean id="metier"
class="istia.st.springioc.troistier.metier.Metier2">
<property name="dao">
<ref local="dao" />
</property>
</bean>
<!-- the UI class -->
<bean id="ui" class="istia.st.springioc.troistier.ui.Ui2">
<property name="metier">
<ref local="metier" />
</property>
</bean>
</beans>
Testprogramme
Das Programm [Main1] lautet wie folgt:
Das Programm [Main1] verwendet die Konfigurationsdatei [spring-config-01.xml] und damit die Implementierungen [Ui1, Metier1, Dao1] der Schichten. Die in der Eclipse-Konsole angezeigten Ergebnisse:
Das Programm [Main2] lautet wie folgt:
Das Programm [Main2] verwendet die Konfigurationsdatei [spring-config-02.xml] und damit die Implementierungen der Schichten [Ui2, Metier2, Dao2]. Die in der Eclipse-Konsole angezeigten Ergebnisse:
15.4. Fazit
Die von uns entwickelte Anwendung ist hochgradig skalierbar. Wir können die Implementierung einer Schicht einfach durch Konfiguration ändern. Der Code für die anderen Schichten bleibt unverändert. Dies wird durch das IoC-Konzept erreicht, das eine der beiden Säulen von Spring darstellt. Die andere Säule ist AOP (Aspect-Oriented Programming), auf die wir hier nicht eingegangen sind. Es ermöglicht Ihnen, einer Klassenmethode – ebenfalls über Konfiguration – „Verhalten“ hinzuzufügen, ohne den Code der Methode zu ändern.






