如何在Java中实现API / SPI模式?

我正在创建一个框架,公开API供开发人员使用:

public interface MyAPI { public void doSomeStuff(); public int getWidgets(boolean hasRun); } 

所有开发人员应该做的是根据这些API方法编写项目代码。 我还希望他们能够在运行时类路径上放置不同的“驱动程序”/“API绑定”(与JDBC或SLF4J的工作方式相同),并让API方法调用( doSomeStuff()等)在不同的第三方上运行资源(文件,服务器等)。 因此,相同的代码和API调用将映射到不同资源上的操作,具体取决于运行时类路径所看到的驱动程序/绑定(即myapi-ftpmyapi-sshmyapi-teleportation )。

如何编写(和打包)允许这种运行时绑定的SPI,然后将MyAPI调用映射到正确的(具体)实现? 换句话说,如果myapi-ftp允许你从FTP服务器getWidgets(boolean) ,我怎么能这样做(使用API​​和SPI)?

具体的工作Java代码示例的奖励点! 提前致谢!

看一下java.util.ServiceLoader类。

一般来说,这个想法是这样的:

API Jar

  • 提供接口
  • 使用ServiceLoader类查找实现
  • 绑定/驱动程序jar

  • 实现界面
  • 创建文件META-INF /并指定实现它的类名
  • 在javadocs中有一个很好的例子:

    http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html

    API Jar

     package com.foo; public interface FooInterface { ... } public class FooInterfaceFactory { public static FooInterface newFooInstance() { ServiceLoader loader = ServiceLoader.load(FooInterface.class); Iterator it = loader.iterator(); // pick one of the provided implementations and return it. } 

    绑定jar子

     package com.myfoo; public class MyFooImpl implements FooInterface { ... } META-INF/com.foo.FooInterface com.myfoo.MyFooImpl 

    编辑

    SPI示例

     public interface FooSpi { void accepts(String url); FooInterface getFooInstance(); } public class FooInterfaceFactory { public static FooInterface getFooInterfaceInstance(String url) { ServiceLoader loader = ServiceLoader.load(FooSpi.class); Iterator it = loader.iterator(); while (it.hasNext()) { FooSpi fooSpi = it.next(); if (fooSpi .accepts(url)) { return fooSpi.getFooInstance(); } } return null; } } 

    当然,将文件名更改为com.foo.FooSpi并提供FooSpi的实现。 这将允许您将公共API与Spi接口隔离开来。

    如果你想隐藏accept方法,你总是可以有第二个接口,它是你的公共API,而t

    嗯,你知道API是客户使用的,而SPI是你的库在内部使用的。

    您将拥有实现API类的类,这些类依赖于SPI接口,并具有一些SPI实现。

    大多数情况下,SPI接口包含低级方法(在您的示例中直接使用FTP,SSH和…的抽象),并且您的库为您的客户端提供更高级别的操作。

    也许你的SPI接口如下:

     public interface ProtocolSPI { boolean isCompatibleWithUrl(String url); Handle connect(String url, Map parameters); int readData(Handle handle, byte[] bytes); void writeData(Handle handle, byte[] bytes, int startIndex, int length); void closeHandle(Handle handle); } 

    并且您有代码依赖于此接口来处理可更换部件。

    您可能有一个ProtocolSPIFactory,它使用java.util.ServiceLoader查找ProtocolSPI的可用实现(在类路径中),然后实例化它们并通过调用isCompatibleWithUrl找出要用于特定url