10

開発者が使用する API を公開するフレームワークを作成しています。

public interface MyAPI {
    public void doSomeStuff();

    public int getWidgets(boolean hasRun);
}

開発者がしなければならないことは、これらの API メソッドに対してプロジェクトをコーディングすることだけです。また、実行時クラスパスにさまざまな「ドライバー」/「API バインディング」を配置し (JDBC や SLF4J の動作と同じように)、API メソッド呼び出し (doSomeStuff()など) をさまざまなサードパーティ リソース (ファイル、サーバーなど)。したがって、同じコードと API 呼び出しが、ランタイム クラスパスが認識するドライバー/バインディング (つまり、、、) に応じて、異なるリソースの操作にマップさmyapi-ftpmyapi-sshますmyapi-teleportation

このようなランタイム バインディングを可能にする SPI を作成 (およびMyAPIパッケージ化) し、呼び出しを正しい (具体的な) 実装にマップするにはどうすればよいですか? 言い換えれば、FTP サーバーからアクセスmyapi-ftpできる場合、getWidgets(boolean)(API と SPI の両方を利用するために) どうすればよいでしょうか?

具体的で機能する Java コード例のボーナスポイント! 前もって感謝します!

4

2 に答える 2

18

java.util.ServiceLoaderクラスを見てください。

一般的に、アイデアはこれです:

APIJar

  • インターフェイスを提供します
  • ServiceLoaderクラスを使用して実装を検索します

    バインディング/ドライバージャー

  • インターフェイスを実装する
  • ファイルMETA-INF/を作成し、それを実装するクラス名を指定します

    javadocsに良い例があります:

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

    APIJar

    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である2番目のインターフェースを常に使用できます。

  • 于 2012-07-10T02:04:41.307 に答える
    3

    APIはクライアントが使用するものであり、SPIはライブラリが内部で使用するものであることをご存知でしょう。

    SPIインターフェイスに依存するAPIクラスを実装するクラスがあり、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);
    }
    

    そして、交換可能な部品を扱うためにこのインターフェースに依存するコードがあります。

    java.util.ServiceLoader(クラスパス内の)ProtocolSPIの利用可能な実装を見つけるために使用するProtocolSPIFactoryがあり、それらをインスタンス化し、を呼び出すことによってisCompatibleWithUrl、特定のに使用する実装を見つけますurl

    于 2012-07-11T03:37:18.307 に答える