Javaを介した一般的なサービス実装のロード。ウティルServiceLoader

StackOverflow https://stackoverflow.com/questions/5451734

  •  12-11-2019
  •  | 
  •  

質問

私は先日、いくつかの不便に遭遇しました java.util.ServiceLoader そして、いくつかの質問が私の中で形成されました。

私が一般的なサービスを持っているとします:

public interface Service<T> { ... }

私は明示的に言うことができませんでした ServiceLoader ロードするには のみ 特定のジェネリック型を持つ実装。

ServiceLoader<Service<String>> services = 
   ServiceLoader.load(Service.class); // Fail.

私の質問は:合理的な使い方は何ですか ServiceLoader 汎用サービスの実装を安全にロードするには?


上記の質問をした後、Paúloの答えの前に、私は解決策を考え出すことができました。

public interface Service<T> { ...
    // true if an implementation can handle the given `t' type; false otherwise.
    public boolean canHandle(Class<?> t) { ...

public final class StringService implements Service<String> { ...
    @Override public boolean canHandle(Class<?> t) {
        if (String.class.isAssignableFrom(type)) 
            return true;
        return false;
    }

public final class DoubleService implements Service<Double> { ...
    // ...

public final class Services { ...
    public static <T> Service<T> getService(Class<?> t) {
        for (Service<T> s : ServiceLoader.load(Service.class))
            if (s.canServe(t))
                return s;
        throw new UnsupportedOperationException("No servings today my son!");
    }

変化する boolean canServe(Class<?> t)boolean canServe(Object o) また、変化する <T> Service<T> getService(Class<?> t) 同じように、より動的にすることができます(私はメソッドを持っていたので、私は自分自身のために後者を使用しています boolean canHandle(T t) 最初の私のインターフェイスで。)

役に立ちましたか?

解決

ここでの問題は、サービスローダーが指定されたクラス/インターフェイスのすべての実装をリストするファイルを使用しており、ファイルはインターフェイス名で命名されていることです。このファイル名にtypeパラメータを入れることは予測されていませんでしたが、ジェネリック型を次のように渡すことも実際には不可能です Class オブジェクト。

したがって、ここでは、任意のタイプの汎用サービスのみを取得し、クラスオブジェクトを調べて、それがサブタイプであるかどうかを確認できます Service<String>.

このようなもの:

class Test{

    public Service<String> getStringService() {
        // it is a bit strange that we can't explicitely construct a
        // parametrized type from raw type and parameters, so here
        // we use this workaround. This may need a dummy method or
        // variable if this method should have another return type.
        ParametrizedType stringServiceType =
            (ParametrizedType)Test.class.getMethod("getStringService").getGenericReturnType();

        ServiceLoader<Service<?>> loader = ServiceLoader.load(Service<?>.class);
        for(Service<?> service : loader) {
           if(isImplementing(service.getClass(), stringServiceType)) {
              @SuppressWarnings("unchecked")
              Service<String> s = (Service)service;
              return s;
           }
        }
    }

    public boolean isImplementing(Class<?> candidate, ParametrizedType t) {
        for(Type iFace : candidate.getGenericInterfaces()) {
           if(iFace.equals(t)) {
              return true;
           }
           if(iFace instanceof ParametrizedType &&
              ((ParametrizedType)iFace).getRawType().equals(t.getRawType())) {
              return false;
           }
        }
        return false;
    }

}

これはテストされておらず、クラスが直接実装するインターフェイスによって拡張されたインターフェイスと、(汎用)スーパークラスによって実装されたインターフェイスを検索するために拡張する必要があるかもしれません。

そしてもちろん、これは次のようなクラスしか見つけることができません

class Example implements Service<String> { ...}

のようなものではありません

class Example<X> implements Service<X> { ... }

ここで Example<String> サービスの有効な実装である可能性があります。

他のヒント

ServiceLoaderクラスファイルをコピーして、load()メソッドからgeneric型引数を削除するだけで、常に機能する。あなたはただ警告を上書きする必要があります。

  public static <S> ServiceLoader load(final Class<S> service)
   {
      return load(service, Thread.currentThread().getContextClassLoader());
   }
.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top