Java Service Provider-Schnittstelle

1. Übersicht

Java 6 hat eine Funktion zum Erkennen und Laden von Implementierungen eingeführt, die einer bestimmten Schnittstelle entsprechen: Service Provider Interface (SPI).

In diesem Tutorial werden wir die Komponenten von Java SPI vorstellen und zeigen, wie wir es auf einen praktischen Anwendungsfall anwenden können.

2. Begriffe und Definitionen von Java SPI

Java SPI definiert vier Hauptkomponenten

2.1. Bedienung

Ein bekannter Satz von Programmierschnittstellen und -klassen, die den Zugriff auf bestimmte Anwendungsfunktionen oder -funktionen ermöglichen.

2.2. Service Provider-Schnittstelle

Eine Schnittstelle oder abstrakte Klasse, die als Proxy oder Endpunkt für den Dienst fungiert.

Wenn der Dienst eine Schnittstelle ist, entspricht er einer Dienstanbieterschnittstelle.

Service und SPI zusammen sind im Java-Ökosystem als API bekannt.

2.3. Dienstleister

Eine spezifische Implementierung des SPI. Der Dienstanbieter enthält eine oder mehrere konkrete Klassen, die den Diensttyp implementieren oder erweitern.

Ein Dienstanbieter wird über eine Anbieterkonfigurationsdatei konfiguriert und identifiziert, die wir im Ressourcenverzeichnis META-INF / services ablegen . Der Dateiname ist der vollständig qualifizierte Name des SPI und sein Inhalt ist der vollständig qualifizierte Name der SPI-Implementierung.

Der Dienstanbieter wird in Form von Erweiterungen installiert, einer JAR-Datei, die wir im Anwendungsklassenpfad, im Java-Erweiterungsklassenpfad oder im benutzerdefinierten Klassenpfad ablegen.

2.4. ServiceLoader

Das Herzstück des SPI ist die ServiceLoader- Klasse. Dies hat die Aufgabe, Implementierungen träge zu erkennen und zu laden. Es verwendet den Kontextklassenpfad, um Anbieterimplementierungen zu finden und in einen internen Cache zu stellen.

3. SPI-Beispiele im Java-Ökosystem

Java bietet viele SPIs. Hier einige Beispiele für die Schnittstelle des Dienstanbieters und den von ihm bereitgestellten Dienst:

  • CurrencyNameProvider: Stellt lokalisierte Währungssymbole für die Währungsklasse bereit .
  • LocaleNameProvider: Stellt lokalisierte Namen für die Locale- Klasse bereit .
  • TimeZoneNameProvider: Stellt lokalisierte Zeitzonennamen für die TimeZone- Klasse bereit .
  • DateFormatProvider: Stellt Datums- und Zeitformate für ein bestimmtes Gebietsschema bereit.
  • NumberFormatProvider: Stellt Geld-, Ganzzahl- und Prozentwerte für die NumberFormat- Klasse bereit .
  • Treiber: Ab Version 4.0 unterstützt die JDBC-API das SPI-Muster. Ältere Versionen verwenden die Class.forName () -Methode, um Treiber zu laden.
  • PersistenceProvider: Bietet die Implementierung der JPA-API.
  • JsonProvider: Stellt JSON-Verarbeitungsobjekte bereit.
  • JsonbProvider: Stellt JSON-Bindungsobjekte bereit.
  • Erweiterung: Stellt Erweiterungen für den CDI-Container bereit.
  • ConfigSourceProvider : Stellt eine Quelle zum Abrufen von Konfigurationseigenschaften bereit.

4. Showcase: eine Anwendung für Wechselkurse

Nachdem wir die Grundlagen verstanden haben, beschreiben wir die Schritte, die zum Einrichten einer Wechselkursanwendung erforderlich sind.

Um diese Schritte hervorzuheben, müssen mindestens drei Projekte verwendet werden: Wechselkurs-API , Wechselkurs-Impl und Wechselkurs-App.

In Unterabschnitt 4.1 werden wir den Service , den SPI und den ServiceLoader über das Modul Wechselkurs-API und dann in Unterabschnitt 4.2 behandeln. wir werden unsere implementieren Service - Provider in dem Wechselkurs-impl - Modul, und schließlich werden wir alles zusammen in Unterabschnitt 4.3 durch das Modul bringen wechselkurs App .

In der Tat können wir so viele Module bieten , wie wir für die brauchen se rvice Anbieter und sie im Classpath des Moduls zur Verfügung zu stellen Wechselkurs-App.

4.1. Erstellen unserer API

Wir beginnen mit der Erstellung eines Maven-Projekts namens Wechselkurs-API . Es ist eine gute Praxis, dass der Name mit dem Begriff API endet , aber wir können ihn wie auch immer nennen.

Dann erstellen wir eine Modellklasse zur Darstellung von Währungen:

package com.baeldung.rate.api; public class Quote { private String currency; private LocalDate date; ... }

Und dann definieren wir unseren Service zum Abrufen von Angeboten, indem wir die Schnittstelle QuoteManager erstellen:

package com.baeldung.rate.api public interface QuoteManager { List getQuotes(String baseCurrency, LocalDate date); }

Als Nächstes erstellen wir einen SPI für unseren Service:

package com.baeldung.rate.spi; public interface ExchangeRateProvider { QuoteManager create(); }

Und schließlich müssen wir eine Dienstprogrammklasse ExchangeRate.java erstellen, die vom Clientcode verwendet werden kann. Diese Klasse wird an ServiceLoader delegiert .

Zuerst rufen wir die statische Factory-Methode load () auf, um eine Instanz von ServiceLoader abzurufen:

ServiceLoader loader = ServiceLoader .load(ExchangeRateProvider.class); 

Und dann rufen wir die iterate () -Methode auf, um alle verfügbaren Implementierungen zu suchen und abzurufen.

Iterator = loader.iterator(); 

Das Suchergebnis wird zwischengespeichert, damit wir die ServiceLoader.reload () -Methode aufrufen können, um neu installierte Implementierungen zu ermitteln:

Iterator = loader.reload(); 

Und hier ist unsere Utility-Klasse:

public class ExchangeRate { ServiceLoader loader = ServiceLoader .load(ExchangeRateProvider.class); public Iterator providers(boolean refresh) { if (refresh) { loader.reload(); } return loader.iterator(); } }

Jetzt, da wir einen Service zum Abrufen aller installierten Implementierungen haben, können wir alle in unserem Client-Code verwenden, um unsere Anwendung zu erweitern, oder nur eine, indem wir eine bevorzugte Implementierung auswählen.

Beachten Sie, dass diese Dienstprogrammklasse nicht Teil des API- Projekts sein muss. Der Clientcode kann die ServiceLoader-Methoden selbst aufrufen .

4.2. Aufbau des Dienstanbieters

Lassen Sie uns nun ein Maven-Projekt mit dem Namen Wechselkurs-Impl erstellen und die API-Abhängigkeit zur pom.xml hinzufügen :

 com.baeldung exchange-rate-api 1.0.0-SNAPSHOT 

Dann erstellen wir eine Klasse, die unser SPI implementiert:

public class YahooFinanceExchangeRateProvider implements ExchangeRateProvider { @Override public QuoteManager create() { return new YahooQuoteManagerImpl(); } }

Und hier die Implementierung der QuoteManager- Oberfläche:

public class YahooQuoteManagerImpl implements QuoteManager { @Override public List getQuotes(String baseCurrency, LocalDate date) { // fetch from Yahoo API } }

Um entdeckt zu werden, erstellen wir eine Provider-Konfigurationsdatei:

META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider 

Der Inhalt der Datei ist der vollständig qualifizierte Klassenname der SPI-Implementierung:

com.baeldung.rate.impl.YahooFinanceExchangeRateProvider 

4.3. Etwas zusammensetzen

Zuletzt erstellen wir ein Client-Projekt namens Wechselkurs-App und fügen die Abhängigkeits-Wechselkurs-API zum Klassenpfad hinzu:

 com.baeldung exchange-rate-api 1.0.0-SNAPSHOT 

An dieser Stelle können wir das SPI aus unserer Anwendung heraus aufrufen :

ExchangeRate.providers().forEach(provider -> ... );

4.4. Ausführen der Anwendung

Konzentrieren wir uns nun auf den Aufbau aller unserer Module:

mvn clean package 

Dann führen wir unsere Anwendung mit dem Java- Befehl aus, ohne den Anbieter zu berücksichtigen:

java -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp

Jetzt werden wir unseren Provider in die Erweiterung java.ext.dirs aufnehmen und die Anwendung erneut ausführen:

java -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:./exchange-rate-impl/target:./exchange-rate-impl/target/depends -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp 

Wir können sehen, dass unser Provider geladen ist.

5. Schlussfolgerung

Nachdem wir den Java SPI-Mechanismus in genau definierten Schritten untersucht haben, sollte klar sein, wie Sie mit Java SPI leicht erweiterbare oder austauschbare Module erstellen können.

Obwohl in unserem Beispiel der Yahoo-Wechselkursdienst verwendet wurde, um die Leistung des Einsteckens in andere vorhandene externe APIs zu demonstrieren, müssen Produktionssysteme nicht auf APIs von Drittanbietern angewiesen sein, um großartige SPI-Anwendungen zu erstellen.

Der Code ist wie üblich auf Github zu finden.