Interface du fournisseur de services Java

1. Vue d'ensemble

Java 6 a introduit une fonctionnalité de découverte et de chargement des implémentations correspondant à une interface donnée: Service Provider Interface (SPI).

Dans ce tutoriel, nous présenterons les composants de Java SPI et montrerons comment nous pouvons l'appliquer à un cas d'utilisation pratique.

2. Termes et définitions de Java SPI

Java SPI définit quatre composants principaux

2.1. Un service

Un ensemble bien connu d'interfaces et de classes de programmation qui permettent d'accéder à certaines fonctionnalités ou fonctionnalités d'application spécifiques.

2.2. Interface du fournisseur de services

Une interface ou une classe abstraite qui agit comme un proxy ou un point de terminaison pour le service.

Si le service est une interface, il est identique à une interface de fournisseur de services.

Le service et le SPI sont bien connus dans l'écosystème Java sous le nom d'API.

2.3. Fournisseur de services

Une implémentation spécifique du SPI. Le fournisseur de services contient une ou plusieurs classes concrètes qui implémentent ou étendent le type de service.

Un fournisseur de services est configuré et identifié via un fichier de configuration de fournisseur que nous mettons dans le répertoire de ressources META-INF / services . Le nom de fichier est le nom complet du SPI et son contenu est le nom complet de l'implémentation SPI.

Le fournisseur de services est installé sous la forme d'extensions, un fichier jar que nous plaçons dans le chemin de classe de l'application, le chemin de classe d'extension Java ou le chemin de classe défini par l'utilisateur.

2.4. ServiceLoader

Au cœur du SPI se trouve la classe ServiceLoader . Cela a pour rôle de découvrir et de charger les implémentations paresseusement. Il utilise le chemin de classe de contexte pour localiser les implémentations de fournisseur et les placer dans un cache interne.

3. Exemples SPI dans l'écosystème Java

Java fournit de nombreux SPI. Voici quelques exemples de l'interface du fournisseur de services et du service qu'il fournit:

  • CurrencyNameProvider: fournit des symboles de devise localisés pour la classe Currency .
  • LocaleNameProvider: fournit des noms localisés pour la classe Locale .
  • TimeZoneNameProvider: fournit des noms de fuseau horaire localisés pour la classe TimeZone .
  • DateFormatProvider: fournit les formats de date et d'heure pour un paramètre régional spécifié.
  • NumberFormatProvider: fournit des valeurs monétaires, entières et en pourcentage pour la classe NumberFormat .
  • Pilote: à partir de la version 4.0, l'API JDBC prend en charge le modèle SPI. Les versions plus anciennes utilisent la méthode Class.forName () pour charger les pilotes.
  • PersistenceProvider: fournit l'implémentation de l'API JPA.
  • JsonProvider: fournit des objets de traitement JSON.
  • JsonbProvider: fournit des objets de liaison JSON.
  • Extension: fournit des extensions pour le conteneur CDI.
  • ConfigSourceProvider : fournit une source pour récupérer les propriétés de configuration.

4. Vitrine: une application de taux de change

Maintenant que nous comprenons les bases, décrivons les étapes nécessaires pour mettre en place une application de taux de change.

Pour mettre en évidence ces étapes, nous devons utiliser au moins trois projets: exchange-rate-api , exchange-rate-impl et exchange-rate-app.

Dans la sous-section 4.1., Nous aborderons le Service , le SPI et le ServiceLoader via le module exchange-rate-api, puis dans la sous-section 4.2. nous allons mettre en œuvre notre service de fournisseur dans l'échange taux impl module, et enfin, nous allons mettre tout ensemble dans la sous-section 4.3 par le module de taux de change app .

En fait, nous pouvons fournir autant de modules que nous avons besoin pour le se rvice fournisseur et les mettre à disposition dans le classpath du module de taux de change app.

4.1. Construire notre API

Nous commençons par créer un projet Maven appelé exchange-rate-api . C'est une bonne pratique que le nom se termine par le terme api , mais nous pouvons l'appeler comme ça.

Ensuite, nous créons une classe modèle pour représenter les taux des devises:

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

Et puis nous définissons notre Service de récupération des devis en créant l'interface QuoteManager:

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

Ensuite, nous créons un SPI pour notre service:

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

Et enfin, nous devons créer une classe utilitaire ExchangeRate.java qui peut être utilisée par le code client. Cette classe délègue à ServiceLoader .

Tout d'abord, nous appelons la méthode de fabrique statique load () pour obtenir une instance de ServiceLoader:

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

Et puis nous invoquons la méthode iterate () pour rechercher et récupérer toutes les implémentations disponibles.

Iterator = loader.iterator(); 

Le résultat de la recherche est mis en cache afin que nous puissions appeler la méthode ServiceLoader.reload () afin de découvrir les implémentations nouvellement installées:

Iterator = loader.reload(); 

Et voici notre classe d'utilité:

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

Maintenant que nous avons un service pour obtenir toutes les implémentations installées, nous pouvons les utiliser toutes dans notre code client pour étendre notre application ou juste une en sélectionnant une implémentation préférée.

Note that this utility class is not required to be part of the api project. Client code can choose to invoke ServiceLoader methods itself.

4.2. Building the Service Provider

Let's now create a Maven project named exchange-rate-impl and we add the API dependency to the pom.xml:

 com.baeldung exchange-rate-api 1.0.0-SNAPSHOT 

Then we create a class that implements our SPI:

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

And here the implementation of the QuoteManager interface:

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

In order to be discovered, we create a provider configuration file:

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

The content of the file is the fully qualified class name of the SPI implementation:

com.baeldung.rate.impl.YahooFinanceExchangeRateProvider 

4.3. Putting It Together

Finally, let's create a client project called exchange-rate-app and add the dependency exchange-rate-api to the classpath:

 com.baeldung exchange-rate-api 1.0.0-SNAPSHOT 

At this point, we can call the SPI from our application:

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

4.4. Running the Application

Let's now focus on building all of our modules:

mvn clean package 

Then we run our application with the Java command without taking into account the 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

Now we'll include our provider in java.ext.dirs extension and we run the application again:

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 

We can see that our provider is loaded.

5. Conclusion

Now that we have explored the Java SPI mechanism through well-defined steps, it should be clear to see how to use the Java SPI to create easily extensible or replaceable modules.

Although our example used the Yahoo exchange rate service to show the power of plugging-in to other existing external APIs, production systems don't need to rely on third-party APIs to create great SPI applications.

Le code, comme d'habitude, peut être trouvé sur Github.