Interface du fournisseur de services Java

Interface du fournisseur de services Java

1. Vue d'ensemble

Java 6 a introduit une fonctionnalité permettant de découvrir et de charger les implémentations correspondant à une interface donnée: SPI (Service Provider Interface).

Dans ce didacticiel, 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

Ensemble bien connu d’interfaces et de classes de programmation permettant d’accéder à une fonctionnalité ou à une fonctionnalité spécifique de l’application.

2.2. Interface du fournisseur de service

Une interface ou une classe abstraite qui agit en tant que proxy ou en tant que point final du service.

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

Ensemble, Service et SPI sont bien connus dans l’écosystème Java en tant qu’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 ressourcesMETA-INF/services. Le nom de fichier est le nom complet du SPI et son contenu est le nom complet de l'implémentation du SPI.

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

2.4. ServiceLoader

Au cœur du SPI se trouve la classeServiceLoader. 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 des fournisseurs 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 service et du service fourni:

4. Vitrine: une application de taux de change

Maintenant que nous comprenons les principes de base, décrivons les étapes nécessaires pour configurer 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, etexchange-rate-app.

In sub-section 4.1., we’ll cover the Service, the SPI, and the ServiceLoader via le moduleexchange-rate-api, puis dans la sous-section 4.2. nous allons implémenter nosservice *provider* dans le modulethe exchange-rate-impl, et enfin, nous allons tout rassembler dans la sous-section 4.3 via le moduleexchange-rate-app.

En fait, nous pouvons fournir autant de modules que nécessaire pour lesserviceprovider et les rendre disponibles dans le classpath du moduleexchange-rate-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 termeapi, mais nous pouvons l'appeler comme ça.

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

package com.example.rate.api;

public class Quote {
    private String currency;
    private LocalDate date;
    ...
}

Et puis nous définissons nosService pour récupérer les citations en créant l'interfaceQuoteManager:

package com.example.rate.api

public interface QuoteManager {
    List getQuotes(String baseCurrency, LocalDate date);
}

Ensuite, nous créons unSPI pour notre service:

package com.example.rate.spi;

public interface ExchangeRateProvider {
    QuoteManager create();
}

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

Tout d'abord, nous invoquons la méthode de fabrique statiqueload() pour obtenir une instance deServiceLoader:

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

Et puis nous invoquons la méthodeiterate() 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éthodeServiceLoader.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 tous les utiliser dans notre code client pour étendre notre application ou une seule en sélectionnant une implémentation préférée.

Il est à noter que cette classe d'utilité n'est pas obligée de faire partie du projetapi. Le code client peut choisir d'appelerServiceLoader methods itself.

4.2. Construire le fournisseur de services

Créons maintenant un projet Maven nomméexchange-rate-impl et nous ajoutons la dépendance API auxpom.xml:


    com.example
    exchange-rate-api
    1.0.0-SNAPSHOT

Ensuite, nous créons une classe qui implémente notre SPI:

public class YahooFinanceExchangeRateProvider
  implements ExchangeRateProvider {

    @Override
    public QuoteManager create() {
        return new YahooQuoteManagerImpl();
    }
}

Et voici l'implémentation de l'interfaceQuoteManager:

public class YahooQuoteManagerImpl implements QuoteManager {

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

Afin d'être découvert, nous créons un fichier de configuration de fournisseur:

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

Le contenu du fichier est le nom de classe complet de l'implémentation SPI:

com.example.rate.impl.YahooFinanceExchangeRateProvider

4.3. Mettre ensemble

Enfin, créons un projet client appeléexchange-rate-app et ajoutons la dépendance exchange-rate-api au classpath:


    com.example
    exchange-rate-api
    1.0.0-SNAPSHOT

À ce stade, nous pouvons appeler le SPI depuis notre application:

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

4.4. Lancer l'application

Concentrons-nous maintenant sur la création de tous nos modules:

mvn clean package

Ensuite, nous exécutons notre application avec la commandeJava sans prendre en compte le fournisseur:

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.example.rate.app.MainApp

Nous allons maintenant inclure notre fournisseur dans l'extensionjava.ext.dirs et nous exécuterons à nouveau l'application:

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.example.rate.app.MainApp

Nous pouvons voir que notre fournisseur est chargé.

5. Conclusion

Maintenant que nous avons exploré le mécanisme Java SPI à travers des étapes bien définies, vous devez savoir comment utiliser le SPI Java pour créer des modules facilement extensibles ou remplaçables.

Bien que notre exemple utilise le service de taux de change Yahoo pour montrer la puissance de la connexion à d'autres API externes existantes, les systèmes de production n'ont pas besoin de s'appuyer sur des API tierces pour créer d'excellentes applications SPI.

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