Interface do provedor de serviços Java

Interface do provedor de serviços Java

1. Visão geral

O Java 6 introduziu um recurso para descobrir e carregar implementações correspondentes a uma determinada interface: Service Provider Interface (SPI).

Neste tutorial, vamos apresentar os componentes do Java SPI e mostrar como podemos aplicá-lo a um caso de uso prático.

2. Termos e definições do Java SPI

Java SPI define quatro componentes principais

2.1. Serviço

Um conjunto bem conhecido de interfaces e classes de programação que fornecem acesso a algumas funcionalidades ou recursos específicos do aplicativo.

2.2. Interface do provedor de serviços

Uma interface ou classe abstrata que atua como um proxy ou um terminal para o serviço.

Se o serviço for uma interface, será o mesmo que uma interface de provedor de serviços.

Serviço e SPI juntos são bem conhecidos no ecossistema Java como API.

2.3. Provedor de serviço

Uma implementação específica do SPI. O provedor de serviços contém uma ou mais classes concretas que implementam ou estendem o tipo de serviço.

Um provedor de serviços é configurado e identificado por meio de um arquivo de configuração de provedor que colocamos no diretório de recursosMETA-INF/services. O nome do arquivo é o nome completo da SPI e seu conteúdo é o nome completo da implementação da SPI.

O provedor de serviços é instalado na forma de extensões, um arquivo jar que colocamos no caminho de classe do aplicativo, no caminho de classe da extensão java ou no caminho de classe definido pelo usuário.

2.4. ServiceLoader

No coração do SPI está a classeServiceLoader. Isso tem o papel de descobrir e carregar implementações lentamente. Ele usa o caminho de classe de contexto para localizar implementações de provedores e colocá-las em um cache interno.

3. Amostras SPI no ecossistema Java

Java fornece muitos SPIs, eis alguns exemplos da interface do provedor de serviços e do serviço que ele fornece:

4. Vitrine: um aplicativo de taxas de câmbio

Agora que entendemos o básico, vamos descrever as etapas necessárias para configurar um aplicativo de taxa de câmbio.

Para destacar essas etapas, precisamos usar pelo menos três projetos:exchange-rate-api,exchange-rate-impl,eexchange-rate-app.

In sub-section 4.1., we’ll cover the Service, the SPI, and the ServiceLoader através do móduloexchange-rate-api, então na subseção 4.2. implementaremos nossoservice *provider* no módulothe exchange-rate-impl e, finalmente, reuniremos tudo na subseção 4.3 por meio do móduloexchange-rate-app.

Na verdade, podemos fornecer quantos módulos precisarmos paraserviceprovidere torná-los disponíveis no caminho de classe do móduloexchange-rate-app.

4.1. Construindo nossa API

Começamos criando um projeto maven chamadoexchange-rate-api. É uma boa prática que o nome termine com o termoapi, mas podemos chamá-lo de qualquer maneira.

Em seguida, criamos uma classe de modelo para representar as taxas das moedas:

package com.example.rate.api;

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

E então definimos nossoService para recuperar as cotações criando a interfaceQuoteManager:

package com.example.rate.api

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

A seguir, criamos umSPI para nosso serviço:

package com.example.rate.spi;

public interface ExchangeRateProvider {
    QuoteManager create();
}

E, finalmente, precisamos criar uma classe de utilitárioExchangeRate.java que pode ser usada pelo código do cliente. Esta classe é delegada aServiceLoader.

Primeiro, invocamos o método de fábrica estáticoload() para obter uma instância deServiceLoader:

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

E então invocamos o métodoiterate() para pesquisar e recuperar todas as implementações disponíveis.

Iterator = loader.iterator();

O resultado da pesquisa é armazenado em cache para que possamos invocar o métodoServiceLoader.reload() para descobrir as implementações instaladas recentemente:

Iterator = loader.reload();

E aqui está nossa classe de utilitários:

public class ExchangeRate {

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

    public Iterator providers(boolean refresh) {
        if (refresh) {
            loader.reload();
        }
        return loader.iterator();
    }
}

Agora que temos um serviço para obter todas as implementações instaladas, podemos usá-las em nosso código de cliente para estender nosso aplicativo ou apenas um selecionando uma implementação preferida.

Deve-se observar que esta classe de utilitário não precisa fazer parte do projetoapi. O código do cliente pode escolher invocarServiceLoader methods itself.

4.2. Construindo o Provedor de Serviços

Vamos agora criar um projeto Maven chamadoexchange-rate-imple adicionar a dependência da API aopom.xml:


    com.example
    exchange-rate-api
    1.0.0-SNAPSHOT

Em seguida, criamos uma classe que implementa nossa SPI:

public class YahooFinanceExchangeRateProvider
  implements ExchangeRateProvider {

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

E aqui a implementação da interfaceQuoteManager:

public class YahooQuoteManagerImpl implements QuoteManager {

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

Para ser descoberto, criamos um arquivo de configuração do provedor:

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

O conteúdo do arquivo é o nome completo da classe da implementação da SPI:

com.example.rate.impl.YahooFinanceExchangeRateProvider

4.3. Juntar as peças

Finalmente, vamos criar um projeto cliente chamadoexchange-rate-appe adicionar a dependência cambial-api ao classpath:


    com.example
    exchange-rate-api
    1.0.0-SNAPSHOT

Neste ponto, podemos chamar o SPI de nosso aplicativo:

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

4.4. Executando o aplicativo

Agora vamos nos concentrar na construção de todos os nossos módulos:

mvn clean package

Em seguida, executamos nosso aplicativo com o comandoJava sem levar em conta o provedor:

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

Agora incluiremos nosso provedor na extensãojava.ext.dirs e executaremos o aplicativo novamente:

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

Podemos ver que nosso provedor está carregado.

5. Conclusão

Agora que exploramos o mecanismo Java SPI por meio de etapas bem definidas, deve ficar claro como usar o Java SPI para criar módulos facilmente extensíveis ou substituíveis.

Embora nosso exemplo tenha usado o serviço de taxa de câmbio do Yahoo para mostrar o poder de se conectar a outras APIs externas existentes, os sistemas de produção não precisam depender de APIs de terceiros para criar ótimos aplicativos SPI.

O código, como de costume, pode ser encontradoover on Github.