Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Neues in Java 7
2 Threads und nebenläufige Programmierung
3 Datenstrukturen und Algorithmen
4 Raum und Zeit
5 Dateien, Verzeichnisse und Dateizugriffe
6 Datenströme
7 Die eXtensible Markup Language (XML)
8 Dateiformate
9 Grafische Oberflächen mit Swing
10 Grafikprogrammierung
11 Netzwerkprogrammierung
12 Verteilte Programmierung mit RMI
13 RESTful und SOAP Web-Services
14 JavaServer Pages und Servlets
15 Applets
16 Datenbankmanagement mit JDBC
17 Technologien für die Infrastruktur
18 Reflection und Annotationen
19 Dynamische Übersetzung und Skriptsprachen
20 Logging und Monitoring
21 Java Native Interface (JNI)
22 Sicherheitskonzepte
23 Dienstprogramme für die Java-Umgebung
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
Java 7 - Mehr als eine Insel von Christian Ullenboom
Das Handbuch zu den Java SE-Bibliotheken
Buch: Java 7 - Mehr als eine Insel

Java 7 - Mehr als eine Insel
Rheinwerk Computing
1433 S., 2012, geb.
49,90 Euro, ISBN 978-3-8362-1507-7
Pfeil 17 Technologien für die Infrastruktur
Pfeil 17.1 Property-Validierung durch Bean Validation
Pfeil 17.1.1 Technische Abhängigkeiten und POJOs
Pfeil 17.2 Wie eine Implementierung an die richtige Stelle kommt
Pfeil 17.2.1 Arbeiten mit dem ServiceLoader
Pfeil 17.2.2 Die Utility-Klasse Lookup als ServiceLoader-Fassade
Pfeil 17.2.3 Contexts and Dependency Injection (CDI) aus dem JSR-299
Pfeil 17.3 Zum Weiterlesen

Rheinwerk Computing - Zum Seitenanfang

17.2 Wie eine Implementierung an die richtige Stelle kommtZur nächsten Überschrift

Je größer eine Java-Anwendung wird, desto größer werden die Abhängigkeiten zwischen Klassen und Typen. Um die Abhängigkeiten zu reduzieren, ist zunächst gewünscht, sich nicht so sehr an Implementierungen zu binden, sondern an Schnittstellen. (Das ist das gelobte »Programmieren gegen Schnittstellen und nicht gegen eine Implementierung.«[106](Wenn etwa eine Klasse C eine Schnittstelle I implementiert und es werden nur Eigenschaften gebraucht, die I bereitstellt, sollte es zum Beispiel I ref = new C(); heißen statt C ref = new C();. Ein Methodenaufruf wie ref.m(); nennt sich dann »gegen« eine Schnittstelle durchgeführt.)) Eine Schnittstelle beschreibt dann Dienste, sogenannte Services, auf die an anderer Stelle zurückgegriffen werden kann.

Die nächste Frage ist, wie Services zusammenfinden. Nehmen wir an, es gibt eine Service-Realisierung für das Drucken und eine andere für die Aufbereitung eines Reports. Wenn wir nun eine Applikation schreiben wollen, die erst auf den Report-Service zugreift und anschließend diesen Report zum Drucker-Service gibt, muss die eigene Applikation irgendwie die Service-Implementierung finden beziehungsweise irgendwie die Service-Implementierung zur Applikation finden. Hier haben sich zwei Konzepte herauskristallisiert:

  • Service-Fabriken: Eine Service-Fabrik ist eine Zentrale, an die sich Interessenten wenden, wenn sie einen Service nutzen wollen. Die Fabrik liefert ein passendes Objekt, das immer eine Service-Schnittstelle implementiert. Welche Realisierung – also konkrete Klasse – die Fabrik liefert, soll den Nutzer nicht interessieren; es geht eben um das Programmieren gegen Schnittstellen.
  • Dependency Injection / Inversion of Control (IoC): Nach diesem Prinzip fragen die Interessenten nicht aktiv über eine zentrale Service-Fabrik nach den Diensten, sondern den Interessenten wird der Service über eine übergeordnete Einheit gegeben (injiziert). Die magische Einheit nennt sich IoC-Container. Es gibt für Injizierungen einen relativ neuen Standard, der im JSR-299 definiert wurde – er wird gleich noch kurz beschrieben. In der Vergangenheit hat sich das Spring-Framework als De-facto-Standard eines IoC-Containers herauskristallisiert. Mittlerweile wird es durch Guice oder auch durch JSR-299-Implementierungen wie die Java EE 6-Container ergänzt.

Rheinwerk Computing - Zum Seitenanfang

17.2.1 Arbeiten mit dem ServiceLoaderZur nächsten ÜberschriftZur vorigen Überschrift

Java SE bietet bisher keine Bibliothek für Dependency Injection, aber mit der Klasse java.util.ServiceLoader eine einfache Realisierung für Service-Fabriken. Ein eigenes Programm soll auf einen Grüßdienst zurückgreifen, aber welche Implementierung das sein wird, soll an anderer Stelle entschieden werden.

Listing 17.13: com/tutego/insel/services/ServiceLoaderDemo, main()

ServiceLoader<Greeter> greeterServices = ServiceLoader.load( Greeter.class );
for ( Greeter greeter : greeterServices )
System.out.println( greeter.getClass() + " : " + greeter.greet( "Chris" ) );

ServiceLoader erfragt mit load() eine Realisierung, die die Schnittstelle Greeter implementieren soll. Die Realisierung ist der Service-Provider. Greeter deklariert eine greet()-Operation:

Listing 17.14: com/tutego/insel/services/Greeter.java

package com.tutego.insel.services;

public interface Greeter
{
String greet( String name );
}

Der Service liefert aber eine konkrete Klasse. Demnach muss es irgendwo eine Zuordnung geben, die einen Typnamen (Greeter) mit einer konkreten Klasse, der Service-Implementierung, verbindet. Dazu ist im Wurzelverzeichnis des Klassenpfades ein Ordner META-INF mit einem Unterordner services anzulegen. In diesem Unterordner ist eine Textdatei (provider configuration file) zu setzen, die den gleichen Namen wie die Service-Schnittstelle besitzt:

META-INF/
services/
com.tutego.insel.services.Greeter

Diese Textdatei, die keine Dateiendung aufweist, enthält Zeilen mit voll qualifizierten Klassennamen (binary name genannt) für die Implementierung, die später hinter diesem Service steht. Es kann eine Zeile oder durchaus mehrere Zeilen für unterschiedliche Implementierungen angegeben sein:

Listing 17.15: META-INF/services/com.tutego.insel.services.Greeter

com.tutego.insel.services.FrisianGreeter

Abbildung

Abbildung 17.1: Vererbungsbeziehung zwischen FrisianGreeter und Greeter

FrisianGreeter ist demnach unsere letzte Klasse und eine tatsächliche Implementierung des Service:

Listing 17.16: com/tutego/insel/services/FrisianGreeter.java

package com.tutego.insel.services;

public class FrisianGreeter implements Greeter
{
@Override public String greet( String name )
{
return "Moin " + name + "!";
}
}
final class java.util.ServiceLoader<S>
implements Iterable<S>
  • static <S> ServiceLoader<S> load(Class<S> service)
    Erzeugt einen neuen ServiceLoader.
  • Iterator<S> iterator()
    Gibt nach und nach alle Services S vom ServiceLoader zurück.

Rheinwerk Computing - Zum Seitenanfang

17.2.2 Die Utility-Klasse Lookup als ServiceLoader-FassadeZur nächsten ÜberschriftZur vorigen Überschrift

So nett der ServiceLoader auch ist, die API könnte ein wenig kürzer sein. Denn oftmals gibt es nur eine Service-Implementierung und nicht gleich mehrere. Daher soll eine Fassade eine knackigere API anbieten. Eine kurze Methode lookup() liefert genau den ersten Service (oder null), und lookupAll() gibt alle Service-Klassen in einer Sammlung zurück. (Das Listing nutzt mehrere Dinge, die die Insel bisher nicht vorgestellt hat! Dazu zählen Datenstrukturen, der Iterator und Meta-Objekte.)

Listing 17.17: com/tutego/insel/services/Lookup.java, Lookup

public class Lookup
{
public static <T> T lookup( Class<T> clazz )
{
Iterator<T> iterator = ServiceLoader.load( clazz ).iterator();
return iterator.hasNext() ? iterator.next() : null;
}

public static <T> Collection<? extends T> lookupAll( Class<T> clazz )
{
Collection<T> result = new ArrayList<T>();
for ( T e : ServiceLoader.load( clazz ) )
result.add( e );
return result;
}
}

Die Nutzung vereinfacht sich damit wie folgt:

Listing 17.18: com/tutego/insel/services/LookupDemo.java, main()

String s = Lookup.lookup( Greeter.class ).greet( "Chris" );
System.out.println( s ); // Moin Chris!
System.out.println( Lookup.lookupAll( Greeter.class ).size() ); // 1

Rheinwerk Computing - Zum Seitenanfang

17.2.3 Contexts and Dependency Injection (CDI) aus dem JSR-299Zur nächsten ÜberschriftZur vorigen Überschrift

Dependency Injection gibt es schon eine ganze Weile, aber erst spät hat sich mit dem JSR-299, »Contexts and Dependency Injection for the Java EE platform«, (kurz CDI) ein Standard herausgebildet. CDI ist Teil jedes Java EE 6-Containers und (bisher) kein Teil von Java SE 7. Dennoch lohnt es sich, diesen Standard etwas näher anzuschauen.

Die CDI-Referenzimplementierung Weld

Wir wollen für ein Beispiel die Referenzimplementierung Weld nutzen, die auch in der Referenzimplementierung GlassFish des Java EE-Standards genutzt wird. Von der Webseite http://seamframework.org/Weld/WeldDistributionDownloads laden wir das aktuelle Weld-Paket (etwa weld-1.1.2.Final.zip mit einer Größe von etwa 20 MiB), packen es aus und nehmen aus dem Verzeichnis artifacts\weld das Java-Archiv weld-se.jar in den Klassenpfad auf. Das war es schon.

CDI-Beispiel

Als Demonstrationsobjekt soll ein Configuration-Objekt in ein Application-Exemplar injiziert werden. Configuration soll globale Konfigurationsinformationen repräsentieren, auf die in der Regel andere Java-Objekte zurückgreifen. Anstatt hier selbst etwa eine Fabrik oder ein Singleton zu programmieren, soll der Container später das Objekt aufbauen und zu den interessierten Objekten bringen.

Listing 17.19: com/tutego/insel/cdi/Configuration.java, Configuration

public class Configuration
{
public String greetingMessage() { return "Willkommen Lena, ähh, CDI "; }
}

Die Klasse Application soll das Hauptprogramm bilden, das schon auf Configuration zurückgreifen möchte:

Listing 17.20: com/tutego/insel/cdi/Application.java, Application

import javax.inject.Inject;

public class Application
{
@Inject Configuration configuration;

public void start()
{
System.out.println( configuration.greetingMessage() );
}
}

Hier können wir die erste Annotation ablesen: @Inject. Dem Objekt Application soll ein Verweis auf das Configuration-Objekt in die Variable configuration injiziert werden. Es kann beliebig viele zu injizierende Attribute geben. Erst danach ist das Application-Objekt korrekt initialisiert. (Das ist wichtig zu verstehen, denn im Konstruktor ist der Zugriff auf die injizierten Objekte verboten, da das Application-Objekt natürlich zuerst über den Standard-Konstruktor erzeugt werden muss, bevor die Objektvariablen initialisiert werden können.) Die Methode start() ist eine selbstgewählte Methode, bei der das gesamte Programm starten soll. Hier lässt sich auf alle injizierten Attribute zurückgreifen, und das Programm testet dies mit einer kleinen Konsolenausgabe.

Damit ist unsere Applikation so weit fertig. Jetzt folgen noch zwei Schritte bis zum erfolgreichen Start:

  1. Wir setzen in unserem Projekt im src-Ordner ein Verzeichnis META-INF und legen dort eine leere Datei mit dem Namen beans.xml an. Sie ist dann nützlich, wenn nicht über Annotationen gearbeitet wird, sondern wenn die Beans extern beschrieben werden sollen. Da wir das nicht nutzen, kann die Datei leer bleiben.[107](Unsinnigerweise muss sie vorhanden sein, auch wenn sie leer ist – vielleicht ändert sich das aber in Zukunft.)
  2. Als Letztes muss die Applikation gestartet werden. Hier unterscheiden sich die CDI-Container ein wenig, und bei Weld gibt es verschiedene Varianten.[108](Dokumentiert unter http://docs.jboss.org/weld/reference/1.0.1-Final/en-US/html/environments.html#d0e5571.) Wir wählen eine Klasse mit einer statischen main()-Methode, die den Weld-Container initialisiert und dann die start()-Methode aufruft, um unser Programm zu starten.

Listing 17.21: com/tutego/insel/cdi/StartMain.java, StartMain, main()

public static void main( String[] args )
{
Application app = new Weld().initialize().instance().select( Application.class ).get();
app.start();
}

Wenn wir das Programm starten, kommen ein paar Logging-Meldungen, die wir jetzt ignorieren wollen, und dann erscheint die erwartete Ausgabe:

Willkommen Lena, ähh, CDI

Der CDI-Standard ist recht umfangreich, und die Weld-Dokumentation http://docs.jboss.org/weld/reference/latest/en-US/html/ gibt einen guten Einblick in die API. Kurz ein paar wichtige Fakten:

  • Anstatt in Objektvariablen zu injizieren, kann der Container auch Setter aufrufen, die mit @Inject annotiert sind. Der Vorteil dabei ist, dass mit dem injizierten Wert gleich etwas gemacht werden kann. Beispielsweise können andere Zustände initialisiert werden.
  • Neben der Injizierung in Attribute und dem Aufruf von Settern kann der Container auch einen Konstruktor aufrufen. Dann darf es aber nur einen parametrisierten Konstruktor mit dem zu injizierenden Objekt geben.
  • Bei der Injizierung erstellt der Container immer ein neues Objekt. Im Fall von unserer Konfiguration muss das nicht sein. Die Annotation @Singleton an einer Klasse weist den Container an, nur einmal das Objekt aufzubauen und dann nur dieses eine Exemplar in alle Injektionspunkte zu spritzen.


Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Neuauflage: Java SE 8 Standard-Bibliothek
Neuauflage: Java SE 8 Standard-Bibliothek
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Java EE 7






 Professionell
 entwickeln mit
 Java EE 7


Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Einstieg in Eclipse






 Einstieg in Eclipse


Zum Katalog: Einstieg in Java






 Einstieg in Java


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2012
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de