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

klsxnrf1  于 2023-02-28  发布在  Java
关注(0)|答案(2)|浏览(101)

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

public interface MyAPI {
    public void doSomeStuff();

    public int getWidgets(boolean hasRun);
}

开发人员所要做的就是根据这些API方法编写项目代码,我还希望他们能够在运行时类路径上放置不同的“驱动程序”/“API绑定(与JDBC或SLF 4J的工作方式相同)并具有API方法调用(doSomeStuff()等)在不同的第三方资源上运行因此,相同的代码和API调用将Map到不同资源上的操作,这取决于运行时类路径看到的驱动程序/绑定(即myapi-ftpmyapi-sshmyapi-teleportation)。
我如何编写(和打包)一个SPI来允许这样的运行时绑定,然后将MyAPI调用Map到正确的(具体的)实现?换句话说,如果myapi-ftp允许您从FTP服务器调用getWidgets(boolean),我如何才能做到这一点(同时使用API和SPI)?
对于具体的、可工作的Java代码示例,我们将给予额外的奖励!提前感谢!

2lpgd968

2lpgd9681#

看一下java.util.ServiceLoader类。
一般来说,这个想法是这样的:
API广口瓶

  • 提供接口
  • 使用ServiceLoader类查找实现

绑定/驱动器罐

  • 实现接口
  • 创建文件META-INF/并指定实现该文件的类名

在javadocs中有一个很好的例子:
http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html

API罐

package com.foo;

public interface FooInterface { ... }

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

绑定罐

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<FooSpi> loader = ServiceLoader.load(FooSpi.class);
    Iterator<FooSpi> 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接口分离。
如果你想隐藏accepts方法,你可以有第二个接口,也就是你的公共API。

polhcujo

polhcujo2#

您知道API是客户端使用的,SPI是库内部使用的。
你应该有实现你的API类的类,这些类依赖于SPI接口,并且有一些SPI的实现。
大多数情况下,SPI接口包含低级方法(在您的示例中,用于直接使用FTP、SSH等的抽象),而您的库为客户端提供更高级的操作。
您的SPI接口可能如下所示:

public interface ProtocolSPI {
    boolean isCompatibleWithUrl(String url);
    Handle connect(String url, Map<String, Object> 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

相关问题