Services und Dependency Injection in der Eclipse 4.0 Application Platform

Werkzeuge  –  Kommentare

Eclipse 4 (e4) bietet eine Fülle von Neuerungen wie das Styling via CSS, das Rendering Framework oder das neue Programmiermodell, das auf Services und Dependency Injection basiert. heise Developer zeigt die Konzepte, die sich hinter dem renovierten Programmiermodell verbergen, und gibt Aufschluss über Vorzüge und Nachteile der neuen Herangehensweise.

In e4 beschreibt das EMF-Modell die Eclipse Workbench. Das bedeutet, dass Komponenten wie Parts, Commands, Menus oder Toolbars in einem Modell verknüpft werden und zur Laufzeit zur Verfügung stehen. Das neue Programmiermodell sieht vor, dass das Framework Modellkomponenten zur Laufzeit auf echte Java-Objekte abbildet. Das gewährleistet, dass es den gesamten Lebenszyklus der Objekte verwalten kann.

Dependency Injection (DI) hat die Welt der Web-Frameworks im Sturm erobert. Es bezeichnet ein Konzept, externe Objekte in eigenen Klassen zur Verfügung zu stellen, ohne über Factories oder den new-Operator neue Instanzen anlegen zu müssen. Stattdessen bietet die Technik Mechanismen, um abhängige Objekte von außen einzuführen beziehungsweise zu injizieren. Im einfachsten Fall definiert eine Klasse setter-Methoden oder Konstruktoren einschließlich benötigter Objektparameter, die sich anschließend von außen aufrufen lassen.

In der Webprogrammierung haben sich die sogenannten DI-Container durchgesetzt: Frameworks, die Objekte und ihre Abhängigkeiten verwalten sowie bestehende Abhängigkeiten bei Bedarf auflösen. Populäre Vertreter sind etwa Spring beziehungsweise Googles Guice. Aktuelle DI-Frameworks setzen typischerweise auf Annotationen, um Abhängigkeiten zu beschreiben. Diesen Ansatz standardisiert der JSR 330 – Dependency Injection for Java –, der unter anderem die folgenden Annotationen spezifiziert.

  • @javax.inject.Inject: Objekt wird vom DI-Container injiziert und lässt sich bei Konstruktoren, Instanzvariablen sowie Methoden verwenden.
  • @javax.inject.Named: Beschreibung des zu injizierenden Objekts

Eclipse e4 stellt eine DI-Implementierung zur Verfügung, die ebenfalls Annotationen aus dem JSR 330 unterstützt.

Vergegenwärtigt sei noch einmal, dass in Eclipse 4.0 das Framework den Lebenszyklus der Modellkomponenten verwaltet. Infolgedessen kann es auf alle Komponenten zugreifen und zusätzlich Annotationen auswerten. Ein Reflection-Mechanismus sorgt dafür, dass sich sowohl Objekte als auch Services in Modellkomponenten injizieren lassen. Allerdings ist das Framework nur in der Lage, Objekte und Services zu injizieren, wenn sie dem Framework bekannt sind.

Wie in anderen DI-Frameworks kann man in e4 Annotationen an unterschiedlichen Stellen im Quellcode platzieren, wodurch beispielsweise die @Inject-Annotation vor Methoden, Konstruktoren oder Instanzvariablen auftreten kann. Die vom DI-Framework verwalteten Objekte und Services referenzieren ein Objekt vom Typ IEclipseContext. Bis auf wenige Ausnahmen wird jedem Objekt aus dem Workbench-Modell ein IEclipseContext zugeordnet. Das bedeutet, dass beispielsweise jedes Window oder jeder Part den eigenen IEclipseContext beansprucht. Der Kontext dient als Registry, in der sich benötigte Objekte und Services ablegen lassen.

Spezifiziert ein Objekt aus dem Modell durch die Inject-Annotation, dass man ein anderes Objekt benötigt, durchsucht das DI-Framework zuerst den lokalen Kontext. Ist die Suche nicht erfolgreich, geht die Suche im Kontext des "Elternelements" weiter. Kontext-Objekte sind demnach hierarchisch gegliedert und haben lokale Service-Objekte. Ein Beispiel soll den Sachverhalt verdeutlichen: Eine UI-Komponente vom Typ View hat einen spezifischen View-Kontext. Die Perspektive, die den View integriert, besitzt ebenfalls einen spezifischen Kontext, der ausschließlich der Perspektive zugeordnet ist. Analog verhält es sich mit Window und dem Window-Kontext. In diesem Kontext befindet sich etwa die aktuelle Selektion der Workbench. Auf oberster Hierarchieebene existiert ein anwendungsweiter Kontext, der unter anderem die OSGi-Services enthält.

Sowohl Java-Objekte als auch OSGi-Services lassen sich beliebigen Modellelementen über DI zur Verfügung stellen. Für Modellelemente funktioniert das automatisch. Darüber hinaus können auch eigene Objekte von DI profitieren.

Die Verwendung von DI hat den Vorteil, dass Modellelemente des Eclipse 4.0 SDK nicht mehr über unterschiedliche Singleton-Klassen der Eclipse-Plattform zugreifen müssen. Somit lassen sich Services der Plattform jederzeit ersetzen. Außerdem vereinfacht sich das Testen der Modellkomponenten erheblich.

Im folgenden Beispiel wird ein Part MyView definiert, der per Annotation dem Framework mitteilt, dass es einen SWT-Container vom Typ Composite und einen OSGi-Service vom Typ Logger benötigt. Der DI-Container sorgt für die korrekte Initialisierung der beiden Instanzvariablen parent und logger. Infolgedessen lassen sich beide Objekte in der buildUI-Methode verwenden. Die @PostConstruct-Annotation zeigt lediglich an, dass man die buildUI-Methode erst nach der Initialisierung durch das DI-Framework ausführen kann.

public class MyView {
      @Inject  // initialisiere die Instanzvariable parent via DI
Composite parent;
@Inject // initialisiere die Instanzvariable logger via DI
Logger logger;
      @PostConstruct  // Methodenaufruf erfolgt erst nach 
//DI-Initialisierung

void buildUI() {
logger.info("erstelle UI");
Label label = new Label(parent, SWT.NONE);
label.setText("Hello World");
}
}

Registriert man eigene OSGi-Services, lassen sie sich ebenso per DI in die Komponente injizieren (siehe Instanzvariable myService unten). Das e4-Programmiermodell erweist sich diesbezüglich als äußerst flexibel. Beispielsweise ersetzt DI dadurch projektspezifische Singleton-Zugriffe, was die Implementierung von JUnit-Tests vereinfacht.

public class MyView {
@Inject
Composite parent;
@Inject
MyService myService; // muss als OSGi-Service registriert sein
     @PostConstruct
void buildUI() {
// erstelle die UI unter Verwendung des injizierten
// Composite und des eigens implementierten OSGi-Service
// MyService

}
}

Einerseits verwendet das Eclipse 4.0 SDK Annotationen, damit das DI-Framework externe Objekte injiziert. Andererseits nutzt es Annotationen aber auch, um dem Framework mitzuteilen, welche Funktion wann im Lebenszyklus der Komponente aufzurufen ist.

  • @org.eclipse.e4.core.di.annotations.Execute: führt die annotierte Methode einer Handler-Klasse aus, sobald der Handler aufgerufen wird.
  • @javax.annotation.PostConstruct: führt die annotierte Methode aus, nachdem die Instanziierung via DI abgeschlossen ist (findet typischerweise bei Methoden zum Erstellen der UI Verwendung).
  • @javax.annotation.PreDestroy: führt die annotierte Methode aus, bevor das Objekt gelöscht wird.