Exemplo de Implementação de SPI (Service Provider Interface) em Java

  1. Definir a interafce do serviço
  2. Proporcionar implementações da interface (com suporte a múltiplas variantes)
  3. Registrar as implementações (configurando em META-INF/services)
  4. Carregar serviços usando ServiceLoader

Demonstração Prática

1. Especificação da Interface

// Caminho do arquivo: src/main/java/br/exemplo/ServicoRegistro.java
package br.exemplo;

public interface ServicoRegistro {
    void registrarMensagem(String texto);
}

2. Criação de Implementações (duas versões distintas)

// Caminho do arquivo: src/main/java/br/exemplo/RegistroConsole.java
package br.exemplo;

public class RegistroConsole implements ServicoRegistro {
    @Override
    public void registrarMensagem(String texto) {
        System.out.println("[Console] " + texto);
    }
}

// Caminho do arquivo: src/main/java/br/exemplo/RegistroArquivo.java
package br.exemplo;

public class RegistroArquivo implements ServicoRegistro {
    @Override
    public void registrarMensagem(String texto) {
        System.out.println("[Arquivo] Registro gravado: " + texto); // Simulação de escrita em arquivo
    }
}

3. Declaração dos Serviços

No diretório de recursos, crie o arquivo:
src/main/resources/META-INF/services/br.exemplo.ServicoRegistro
Inclua os nomes completos das classes implementadoras:

br.exemplo.RegistroConsole
br.exemplo.RegistroArquivo

4. Utilização dos Serviços

// Caminho do arquivo: src/main/java/br/exemplo/AplicacaoPrincipal.java
package br.exemplo;

import java.util.ServiceLoader;

public class AplicacaoPrincipal {
    public static void main(String[] argumentos) {
        // Carrega todas as implementações de ServicoRegistro
        ServiceLoader<ServicoRegistro> provedores = ServiceLoader.load(ServicoRegistro.class);
        
        // Itera e executa cada serviço
        for (ServicoRegistro provedor : provedores) {
            provedor.registrarMensagem("Olá via SPI!");
        }
    }
}

Saída Esperada

[Console] Olá via SPI!
[Arquivo] Registro gravado: Olá via SPI!

Aspectos Fundamentais

  1. Localização do arquivo de registro
    META-INF/services/nome.completo.da.interface (sem extensão)
  2. Formato do conteúdo
    Cada linha contém o nome completo de uma classe implementadora (separadas por linhas para múltiplas opções)
  3. Funcionamento do carregamento
    O ServiceLoader busca registros em META-INF/services usando o carregador de classes do contexto da thread
  4. Cenários de aplicação
    • Carregamento de drivers JDBC (via DriverManager)
    • Fachada de logging como SLF4J
    • Configuração automática no Spring Boot

Comparação com Chamada Direta de Interface

Aspecto Mecanismo SPI Chamada Direta de Interface
Acoplamento Implementações podem ser estendidas de forma independente, com desacoplamento total Exige dependência explícita de uma implementação específica
Adição de novas implementações Basta adicionar um JAR com as classes, e elas são reconhecidas automaticamente Necessita modificar o código do consumidor
Relações de dependência O consumidor depende apenas da interface O consumidor depende da interface e de uma implementação concreta

No ecossistema Spring Boot, a estrutura spring.factories é uma evolução do SPI, com princípios semelhentes mas suporte a cenários de configuração automática mais complexos.

Tags: java SPI ServiceLoader desacoplamento Extensão Dinâmica

Publicado em 7-22 21:09