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:
-
CurrencyNameProvider: fournit des symboles monétaires localisés pour la classeCurrency.
-
LocaleNameProvider: fournit des noms localisés pour la classeLocale.
-
TimeZoneNameProvider: fournit des noms de fuseau horaire localisés pour la classeTimeZone.
-
DateFormatProvider: fournit des 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 classeNumberFormat.
-
Driver: à partir de la version 4.0, l'API JDBC prend en charge le modèle SPI. Les versions plus anciennes utilisent la méthodeClass.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.
-
Extention: 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 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.