OSGi goes Enterprise Teil 2: Dependency Injection

Dependency Injection mit Gemini Blueprint

Jan Stamer

Die Zwillinge Kastor und Polydeukes aus dem Sternbild Zwillinge (engl. Gemini) waren unzertrennlich. Weil Polydeukes nicht ohne seinen Bruder sein konnte, teilte er sogar seine Unsterblichkeit mit ihm. Wir haben etwas falsch gemacht, wenn unsere Enterprise-Anwendung genauso unzertrennlich ist. Sie soll lose gekoppelt sein. So können einzelne Teile flexibler und besser wiederverwendet werden. In diesem Artikel lernen wir, wie wir mit Gemini Blueprint dem Ziel von lose gekoppelten Enterprise-Modulen näher kommen.

OSGi goes Enterprise

Der Artikel „Dependency Injection mit Gemini Blueprint“ ist Teil der Serie „OSGi goes Enterprise, erstmals erschienen im Eclipse Magazin.

Im ersten Teil dieser Artikelserie haben wir mit der Persistenzschicht den Grundstein unserer Anwendung gelegt. Sie ist modular aufgebaut und besteht aus OSGi Bundles mit JPA-Entitäten. Diese Bundles mit JPA-Entitäten und ein paar Metainformationen werden mithilfe der Eclipse-Gemini-Projekte JPA und DBAccess zu einer lauffähigen Implementierung. Sie ist unabhängig von einer konkreten Datenbank oder einer bestimmten JPA-Implementierung. Von Transaktionen war allerdings bis jetzt noch keine Rede. Diese brauchen wir jedoch zwingend, denn sonst läuft nichts in Richtung Datenbank. Doch bevor wir uns an die Steuerung der Transaktionen machen, gibt es noch ein essenzielles Thema zu klären: Wie gehen wir mit Abhängigkeiten zwischen Services und Bundles um? Damit beschäftigen wir uns in diesem Artikel. Aber keine Sorge, die Steuerung der Transaktionen steht gleich danach auf dem Programm, nämlich im nächsten Artikel dieser Serie.

Wozu Dependency Injection?

Was ist denn eigentlich Dependency Injection und wozu brauchen wir das? Es handelt sich um einen Konfigurationsmechanismus, um die Implementierung eines Interface zur Laufzeit statt zur Compilezeit festzulegen. Dazu gibt es ein kleines Beispiel aus unserer Anwendung, die bei Github [1] liegt. Die Anwendung verwaltet Kunden (Customer) und Konten (Account). Betrachten wir den Zugriff auf die Kunden: Er erfolgt über das Interface CustomerRepository, das Methoden bietet, um Kunden zu lesen und zu speichern. In unserer Enterprise-Anwendung wollen wir Kunden und Konten verwalten, unabhängig davon, ob diese in einer PostgresSql-Datenbank oder in der MongoDB abgelegt sind. Die Implementierung unserer Anwendung soll unabhängig sein von der konkreten Implementierung der Persistenzschicht. Das heißt, unsere Anwendung programmieren wir ausschließlich gegen die Interfaces Customer, Account und die darauf aufbauenden Repositories CustomerRepository und AccountRepository. Zur Entwicklung reichen uns die Interfaces noch. Zur Laufzeit brauchen wir jedoch auch eine konkrete Implementierung dieser Interfaces, damit etwas passiert.

Eine Implementierung der Interfaces haben wir schon, nämlich auf Basis von JPA-Entitäten aus dem vorherigen Artikel. Unsere Anwendung enthält auch ein User Interface, in der mit Code ähnlich Listing 1 eine Tabelle der Kunden dargestellt wird.

Listing 1
public class TableView implements ViewContribution {
 private CustomerRepository customerRepository;
 public void refreshTable() {
   for (Customer person : customerRepository.findAll()) {
     table.addItem(person);
   }
 }
}

Aber jetzt kommt der Knackpunkt: Wie kommen wir dort an die konkrete Implementierung des Interface CustomerRepository? Beamen wir uns kurz in die „normale“ Java-Welt ohne OSGi. Dort könnten wir das Problem wie folgt lösen:

private CustomerRepository customerRepository = new CustomerRepositoryImpl();

Jetzt beamen wir uns wieder zurück in die OSGi-Welt. Und die ist böse – Eclipse meldet uns den Fehler „Class CustomerRepositoryImpl not visible“. Die konkrete Implementierung des Interfaces CustomerRepository für JPA ist aus dem Bundle mit der UI-Klasse nicht sichtbar. Der Grund dafür ist, dass das Bundle mit der UI-Klasse von dem Bundle mit dem Interface CustomerRepository abhängt, nicht jedoch von einer konkreten Implementierung dieses Interface. Genau das ist nämlich der Fall gewesen, als wir OSGi außen vor gelassen haben und direkt eine konkrete Instanz der Klasse CustomerRepositoryImpl erzeugt haben. Solche Abhängigkeiten führen zu einer engen Koppelung der Module. Genau das wollen wir in unserer Anwendung vermeiden. OSGi hilft uns dabei, indem es dafür sorgt, dass jedes Bundle auch tatsächlich nur das sieht, was es sehen soll. Aber wie kommt die konkrete Implementierung des Interface CustomerRepository in unsere UI-Klasse? Eine Möglichkeit ist, sie zu „injecten“, also zur Laufzeit per Dependency Injection zu konfigurieren. Die Abhängigkeit wird also nicht mehr zur Compilezeit im Code festgelegt, sondern zur Laufzeit konfiguriert. Die OSGI-Enterprise-Spezifikation führt in der Version 4.2 einen neuen Standard für DI ein, die Blueprint Services. In den nächsten Abschnitten werden wir sehen, was dahintersteckt.

Geschrieben von
Jan Stamer
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.