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

IAm*_*aja 9 java api design-patterns slf4j

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

public interface MyAPI {
    public void doSomeStuff();

    public int getWidgets(boolean hasRun);
}
Run Code Online (Sandbox Code Playgroud)

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

我怎样写(和封装)的SPI允许这样的运行时绑定,再映射MyAPI调用正确的(混凝土)执行?换句话说,如果myapi-ftp允许你getWidgets(boolean)从FTP服务器,我怎么能这样(使用API​​和SPI)?

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

Mat*_*att 16

看一下java.util.ServiceLoader类.

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

API Jar

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

    绑定/驱动程序罐

  • 实现接口
  • 创建文件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<FooInterface> loader = ServiceLoader.load(FooInterface.class);
        Iterator<FooInterface> it = loader.iterator();
        // pick one of the provided implementations and return it.
      }
    
    Run Code Online (Sandbox Code Playgroud)

    绑定罐子

    package com.myfoo;
    public class MyFooImpl implements FooInterface { ... }
    
    META-INF/com.foo.FooInterface
        com.myfoo.MyFooImpl
    
    Run Code Online (Sandbox Code Playgroud)

    编辑

    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;
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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