Java SE 8 Standard-Bibliothek  
Professionelle Bücher. Auch für Einsteiger.
 
Inhaltsverzeichnis

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

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 16 Technologien für die Infrastruktur
Pfeil 16.1 Property-Validierung durch Bean Validation
Pfeil 16.1.1 Technische Abhängigkeiten und POJOs
Pfeil 16.2 Wie eine Implementierung an die richtige Stelle kommt
Pfeil 16.2.1 Arbeiten mit dem ServiceLoader
Pfeil 16.2.2 Die Utility-Klasse Lookup als ServiceLoader-Fassade
Pfeil 16.2.3 Contexts and Dependency Injection (CDI) aus dem JSR-299
Pfeil 16.3 Zum Weiterlesen
 
Zum Seitenanfang

16.2Wie eine Implementierung an die richtige Stelle kommt Zur vorigen ÜberschriftZur 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.«[ 129 ](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, so genannte 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 bzw. 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-Container ergänzt.

 
Zum Seitenanfang

16.2.1Arbeiten mit dem ServiceLoader Zur vorigen ÜberschriftZur nächsten Ü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 16.13com/tutego/insel/services/ServiceLoaderDemo.java, main()

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

ServiceLoader erfragt mit load(Class<S> service [, ClassLoader loader]) eine Realisierung, die die Schnittstelle Greeter implementieren soll. Die Realisierung ist der Service-Provider. Greeter deklariert eine greet(String)-Operation:

Listing 16.14com/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 16.15META-INF/services/com.tutego.insel.services.Greeter

com.tutego.insel.services.FrisianGreeter
Vererbungsbeziehung zwischen FrisianGreeter und Greeter

Abbildung 16.1Vererbungsbeziehung zwischen FrisianGreeter und Greeter

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

Listing 16.16com/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.

 
Zum Seitenanfang

16.2.2Die Utility-Klasse Lookup als ServiceLoader-Fassade Zur vorigen ÜberschriftZur nächsten Ü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 eigene kurze Methode lookup(Class) liefert genau den ersten Service (oder null), und lookupAll(Class) 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 16.17com/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<>();
for ( T e : ServiceLoader.load( clazz ) )
result.add( e );
return result;
}
}

Die Nutzung vereinfacht sich damit wie folgt:

Listing 16.18com/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
 
Zum Seitenanfang

16.2.3Contexts and Dependency Injection (CDI) aus dem JSR-299 Zur vorigen ÜberschriftZur nächsten Ü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-Containers und (bisher) kein Teil der Java SE. 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-2.0.3.Final.zip mit einer Größe von fast 50 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 16.19com/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 16.20com/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 selbst gewä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.[ 130 ](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.[ 131 ](Dokumentiert unter http://docs.jboss.org/weld/reference/latest/en-US/html/environments.html.) Wir wählen eine Klasse mit einer statischen main(String[])-Methode, die den Weld-Container initialisiert und dann die start()-Methode aufruft, um unser Programm zu starten:

Listing 16.21com/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 ist dabei, 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.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2018. Original - https://www.rheinwerk-verlag.de/openbook/
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook 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.

 

 
 


25.04.2024 - Sitemap.xml