Java-serviceproviderinterface

1. Overzicht

Java 6 heeft een functie geïntroduceerd voor het ontdekken en laden van implementaties die overeenkomen met een bepaalde interface: Service Provider Interface (SPI).

In deze tutorial introduceren we de componenten van Java SPI en laten we zien hoe we deze kunnen toepassen op een praktisch gebruik.

2. Termen en definities van Java SPI

Java SPI definieert vier hoofdcomponenten

2.1. Onderhoud

Een bekende set programmeerinterfaces en klassen die toegang bieden tot een specifieke toepassingsfunctionaliteit of -functie.

2.2. Serviceproviderinterface

Een interface of abstracte klasse die fungeert als een proxy of een eindpunt voor de service.

Als de service één interface is, is deze hetzelfde als een serviceproviderinterface.

Service en SPI samen zijn in het Java Ecosystem bekend als API.

2.3. Dienstverlener

Een specifieke implementatie van de SPI. De serviceprovider bevat een of meer concrete klassen die het servicetype implementeren of uitbreiden.

Een serviceprovider wordt geconfigureerd en geïdentificeerd via een providerconfiguratiebestand dat we in de resourcemap plaatsen META-INF / diensten. De bestandsnaam is de volledig gekwalificeerde naam van de SPI en de inhoud ervan is de volledig gekwalificeerde naam van de SPI-implementatie.

De Service Provider wordt geïnstalleerd in de vorm van extensies, een jar-bestand dat we in het klassepad van de toepassing plaatsen, de Java-extensie klassenpad of het door de gebruiker gedefinieerde klassenpad.

2.4. ServiceLoader

De kern van de SPI is de ServiceLoader klasse. Dit heeft de rol van het lui ontdekken en laden van implementaties. Het gebruikt het contextklassenpad om providerimplementaties te lokaliseren en deze in een interne cache te plaatsen.

3. SPI-voorbeelden in het Java-ecosysteem

Java biedt veel SPI's. Hier zijn enkele voorbeelden van de serviceproviderinterface en de service die deze biedt:

  • ValutaNaamProvider: biedt gelokaliseerde valutasymbolen voor de Valuta klasse.
  • LocaleNameProvider: biedt gelokaliseerde namen voor de Locale klasse.
  • TimeZoneNameProvider: biedt gelokaliseerde tijdzonenamen voor de Tijdzone klasse.
  • DateFormatProvider: biedt datum- en tijdnotaties voor een opgegeven landinstelling.
  • NumberFormatProvider: biedt monetaire, gehele getallen en percentagewaarden voor de Nummer formaat klasse.
  • Bestuurder: vanaf versie 4.0 ondersteunt de JDBC API het SPI-patroon. Oudere versies gebruiken de Class.forName () methode om stuurprogramma's te laden.
  • PersistenceProvider: verzorgt de implementatie van de JPA API.
  • JsonProvider: biedt JSON-verwerkingsobjecten.
  • Leverancier: biedt JSON-bindende objecten.
  • Uitbreiding: biedt uitbreidingen voor de CDI-container.
  • ConfigSourceProvider: biedt een bron voor het ophalen van configuratie-eigenschappen.

4. Showcase: een toepassing voor wisselkoersen

Nu we de basis begrijpen, gaan we de stappen beschrijven die nodig zijn om een ​​wisselkoersapplicatie in te stellen.

Om deze stappen te benadrukken, hebben we ten minste drie projecten nodig: wisselkoers-api, wisselkoers-impl, en wisselkoers-app.

In paragraaf 4.1. Behandelen we de Onderhoud, de SPI en de ServiceLoadervia de module wisselkoers-api, vervolgens in paragraaf 4.2. we implementeren onze onderhoud provider in de wisselkoers-impl module, en tot slot zullen we alles samenbrengen in paragraaf 4.3 via de module wisselkoers-app.

In feite kunnen we zoveel modules leveren als we nodig hebben voor het service provider en maak ze beschikbaar in het klassenpad van de module wisselkoers-app.

4.1. Onze API bouwen

We beginnen met het maken van een Maven-project met de naam wisselkoers-api. Het is een goede gewoonte dat de naam eindigt met de term api, maar we kunnen het hoe dan ook noemen.

Vervolgens maken we een modelklasse voor het weergeven van koersvaluta's:

pakket com.baeldung.rate.api; openbare klasse Citaat {privé String-valuta; privé LocalDate-datum; ...}

En dan definiëren we onze Onderhoud voor het ophalen van offertes door de interface te maken Offerte Manager:

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

Vervolgens maken we een SPI voor onze service:

pakket com.baeldung.rate.spi; openbare interface ExchangeRateProvider {QuoteManager create (); }

En tot slot moeten we een hulpprogramma-klasse maken ExchangeRate.java die kan worden gebruikt door klantcode. Deze klas delegeert naar ServiceLoader.

Ten eerste doen we een beroep op de statische fabrieksmethode laden() om een ​​exemplaar van ServiceLoader:

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

En dan roepen we de herhalen() methode om alle beschikbare implementaties te zoeken en op te halen.

Iterator = loader.iterator (); 

Het zoekresultaat wordt in de cache opgeslagen, zodat we de ServiceLoader.reload () methode om nieuw geïnstalleerde implementaties te ontdekken:

Iterator = loader.reload (); 

En hier is onze utility-klasse:

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

Nu we een service hebben om alle geïnstalleerde implementaties te krijgen, kunnen we ze allemaal in onze clientcode gebruiken om onze applicatie uit te breiden of slechts één door een voorkeursimplementatie te selecteren.

Merk op dat deze utility-klasse geen onderdeel hoeft te zijn van de api project. Klantcode kan ervoor kiezen om aan te roepen ServiceLoader werkt zelf.

4.2. Bouwen aan de serviceprovider

Laten we nu een Maven-project maken met de naam wisselkoers-impl en we voegen de API-afhankelijkheid toe aan het pom.xml:

 com.baeldung wisselkoers-api 1.0.0-SNAPSHOT 

Vervolgens maken we een klasse die onze SPI implementeert:

openbare klasse YahooFinanceExchangeRateProvider implementeert ExchangeRateProvider {@Override openbare QuoteManager create () {retourneer nieuwe YahooQuoteManagerImpl (); }}

En hier de implementatie van de QuoteManager koppel:

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

Om ontdekt te worden, maken we een providerconfiguratiebestand aan:

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

De inhoud van het bestand is de volledig gekwalificeerde klassenaam van de SPI-implementatie:

com.baeldung.rate.impl.YahooFinanceExchangeRateProvider 

4.3. Het samenvoegen

Laten we tot slot een klantproject maken met de naam wisselkoers-app en voeg de afhankelijkheid exchange-rate-api toe aan het klassenpad:

 com.baeldung wisselkoers-api 1.0.0-SNAPSHOT 

Op dit punt kunnen we de SPI bellen vanuit onze applicatie:

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

4.4. De applicatie uitvoeren

Laten we ons nu concentreren op het bouwen van al onze modules:

mvn schoon pakket 

Vervolgens voeren we onze applicatie uit met de Java commando zonder rekening te houden met de provider:

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

Nu nemen we onze provider op in java.ext.dirs extensie en we voeren de applicatie opnieuw uit:

java -Djava.ext.dirs = $ JAVA_HOME / jre / lib / ext: ./ exchange-rate-impl / target: ./ exchange-rate-impl / target / afhankelijk -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 

We kunnen zien dat onze provider is geladen.

5. Conclusie

Nu we het Java SPI-mechanisme hebben verkend door middel van goed gedefinieerde stappen, zou het duidelijk moeten zijn hoe we de Java SPI kunnen gebruiken om eenvoudig uitbreidbare of vervangbare modules te maken.

Hoewel ons voorbeeld de Yahoo-wisselkoersservice gebruikte om te laten zien hoe krachtig het is om in te pluggen op andere bestaande externe API's, hoeven productiesystemen niet te vertrouwen op API's van derden om geweldige SPI-applicaties te maken.

De code is, zoals gewoonlijk, te vinden op Github.