Java 服务提供程序接口 (SPI)— 代码实践

概述

你是否曾经想过JPA实现(EclipseLink或Hibernate)是如何被JPA获得的。或者java.sql.DriverManager是如何加载java.sql.Driver的可用实现。又或者就此而言,消息传递框架如何获取JMS实现。

其实他们都遵循JAVA SPI(服务提供商接口)机制。该机制是Java 6引入了一个特性,用于服务的发现和加载。

摘自Effective Java第二版 :

一个服务提供框架是一个由服务被多个服务提供者实现的系统,系统使其客户端可以使用这些实现,从而将它们与实现解耦。

组件

  1. 服务提供接口(Service Provider Interface)
public interface IGetIdService {
    long getId();
}

按照SPI标准,我们必须在META-INF/services资源目录下定义一个以接口完全限定名命名的配置文件。

  1. 服务提供实现(Service Provider Implementation)
public class GetIdServiceImpl1 implements IGetIdService {

    @Override
    public long getId() {
        return 1L;
    }
}


public class GetIdServiceImpl2 implements IGetIdService {
    @Override
    public long getId() {
        return 2L;
    }
}

同样,服务提供实现也必须在上述配置文件中以实现类的全限定名配置

com.dek.service.impl.GetIdServiceImpl1
com.dek.service.impl.GetIdServiceImpl2
  1. ServiceLoader
    SPI的核心是java.util.ServiceLoader类。它的作用是发现服务并懒加载。它使用上下文类路径来定位提供者实现,并将它们放在内部缓存中。

测试

  1. 调用ServiceLoader的静态工厂方法load()获取一个ServiceLoader的实例
ServiceLoader loaders = ServiceLoader.load(IGetIdService.class);
  1. 调用 iterate() 方法获取服务所有可用的实现。
Iterator it = loaders.iterator();
  1. 这个结果会被缓存起来,当有新的实现时,可以调用ServiceLoader.reload() 刷新结果。
Iterator = loaders.reload();

完整代码如下:

public class GetIdTest {

    @Test
    public void spiTest() {
        ServiceLoader loaders = ServiceLoader.load(IGetIdService.class);
        Iterator it = loaders.iterator();

        while (it.hasNext()) {
            System.out.println(it.next().getId());
        }
    }
}

源码

你可能感兴趣的:(Java 服务提供程序接口 (SPI)— 代码实践)