web-dev-qa-db-ja.com

ランタイム「修飾子」変数を使用してサービスを動的に注入する方法は?

ランタイム値を指定してコンポーネント/サービスを注入する簡単な方法が見つかりません。

@ Springのドキュメントを読み始めました: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-autowired-annotation- qualifiers しかし、@ Qualifierアノテーションに渡される値を可変化する方法はありません。

このようなインターフェイスを持つモデルエンティティがあるとします。

public interface Case {

    String getCountryCode();
    void setCountryCode(String countryCode);

}

私のクライアントコードでは、次のようなことをします。

@Inject
DoService does;

(...)

Case myCase = new CaseImpl(); // ...or whatever
myCase.setCountryCode("uk");

does.whateverWith(myCase);

...私のサービスで:

@Service
public class DoService {

    @Inject
    // FIXME what kind of #$@& symbol can I use here?
    // Seems like SpEL is sadly invalid here :(
    @Qualifier("${caze.countryCode}")
    private CaseService caseService;

    public void whateverWith(Case caze) {
        caseService.modify(caze);
    }

}

CaseServiceがUKCaseServiceになることを期待しています(以下の関連コードを参照)。

public interface CaseService {

    void modify(Case caze);

}

@Service
@Qualifier("uk")
public class UKCaseService implements CaseService {

}

@Service
@Qualifier("us")
public class USCaseService implements CaseService {

}

したがって、いずれかの/すべてのSpring機能を使用して、これらのすべてを最も単純/エレガント/効率的な方法で「修正」するにはどうすればよいですか。しかし、SpringはcaseServiceを注入する前に「ケース」を知る必要があるため、DoServiceで何かが間違っているとすでに疑っています...しかし、クライアントコードがcaseServiceを知らずにこれを達成する方法は?私はこれを理解することはできません...

私はここでいくつかの問題をすでに読んでいますが、ほとんどの場合、彼らは実際に私と同じニーズおよび/または構成を持っていないか、投稿された答えは私に満足していません(彼らは本質的に回避策または(古い)(古い)Spring機能の使用)。

複数の一致するBeanが見つかった場合、Springは名前でどのように自動配線しますか? =>はコンポーネントのようなクラスのみを参照します

Springで自動配線するBeanを動的に定義する(修飾子を使用) =>本当に興味深いが、最も精巧な答え(4票)は...ほぼ3 1 /2歳?! (2013年7月)

Spring 3-別のオブジェクト属性に基づいた実行時の動的自動配線 =>ここでは非常に類似した問題ですが、答えは実際の回避策ではなく実際のデザインパターンのように見えます(工場のような)?そして、私はすべてのコードをServiceImplに実装するのが好きではありません...

春@Autowiring、オブジェクトファクトリを使用して実装を選択する方法? => 2番目の答えは興味深いようですが、その作者は展開しませんので、私は知っています(少し)Java Config&stuff、彼が何について話しているのかよくわかりません...

XMLを使用せずにSpringでプロパティに基づいて実行時に異なるサービスを注入する方法 =>興味深い議論、特に答えですが、ユーザーにはプロパティセットがありますが、私にはありません。

こちらもお読みください: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-bean-references =>式での「@」の使用に関する拡張例を見つけることができません。誰かがこれを知っていますか?

編集:他の同様の関連する問題が見つかりましたが、誰も適切な答えを得ていません: @ Autowiredを使用してファクトリパターンのような実装を動的に注入する方法Spring修飾子とプロパティプレースホルダーSpring:プロパティプレースホルダーで@Qualifierを使用するSpringで条件付き自動配線を行う方法は?Springでの動的注入@ QualifierのSpELは同じBeanを参照しますSpELを使用してSpringでメソッド呼び出しの結果を注入する方法?

工場パターンは解決策かもしれませんか? @ Autowiredを使用して、ファクトリパターンのような実装を動的に挿入する方法

18
maxxyme

BeanFactory を使用すると、名前からコンテキストからBeanを動的に取得できます。

_@Service
public class Doer {

  @Autowired BeanFactory beans;

  public void doSomething(Case case){
    CaseService service = beans.getBean(case.getCountryCode(), CaseService.class)
    service.doSomething(case);
  }
}
_

サイドノート。Bean名として国コードのようなものを使用するのは少し奇妙に見えます。少なくともいくつかのプレフィックスを追加するか、他のデザインパターンを検討してください。

それでも国ごとに豆を持ちたい場合は、別のアプローチをお勧めします。レジストリサービスを導入して、国コードで必要なサービスを取得します。

_@Service
public class CaseServices {

  private final Map<String, CaseService> servicesByCountryCode = new HashMap<>();

  @Autowired
  public CaseServices(List<CaseService> services){
    for (CaseService service: services){
      register(service.getCountryCode(), service);
    }
  }

  public void register(String countryCode, CaseService service) {
    this.servicesByCountryCode.put(countryCode, service);
  }

  public CaseService getCaseService(String countryCode){
    return this.servicesByCountryCode.get(countryCode);
  }
}
_

使用例:

_@Service
public class DoService {

  @Autowired CaseServices caseServices;

  public void doSomethingWith(Case case){
    CaseService service = caseServices.getCaseService(case.getCountryCode());
    service.modify(case);
  }
}
_

この場合、CaseServiceインターフェイスにString getCountryCode()メソッドを追加する必要があります。

_public interface CaseService {
    void modify(Case case);
    String getCountryCode();
}
_

または、メソッドCaseService.supports(Case case)を追加してサービスを選択できます。または、インターフェイスを拡張できない場合は、何らかの初期化子または_@Configuration_クラスからCaseServices.register(String, CaseService)メソッドを呼び出すことができます。

UPDATE:言及するのを忘れて、Springはすでにニースを提供している PluginPluginRegistryを作成するための定型コードを再利用するための抽象化このような。

例:

_public interface CaseService extends Plugin<String>{
    void doSomething(Case case);
}

@Service
@Priority(0)
public class SwissCaseService implements CaseService {

  void doSomething(Case case){
    // Do something with the Swiss case
  }

  boolean supports(String countryCode){
    return countryCode.equals("CH");
  }
}

@Service
@Priority(Ordered.LOWEST_PRECEDENCE)
public class DefaultCaseService implements CaseService {

  void doSomething(Case case){
    // Do something with the case by-default
  }

  boolean supports(String countryCode){
    return true;
  }
}

@Service
public class CaseServices {

  private final PluginRegistry<CaseService<?>, String> registry;

  @Autowired
  public Cases(List<CaseService> services){
    this.registry = OrderAwarePluginRegistry.create(services);
  }

  public CaseService getCaseService(String countryCode){
    return registry.getPluginFor(countryCode);
  }
}
_
17
aux

これによればSO answer、using @Qualifierを使用してもあまり役に立ちません: 修飾子でApplicationContextからBeanを取得します

代替戦略として:

  • スプリングブートの場合は、 @ConditonalOnProperty または別のConditionalを使用できます。

  • @auxが示唆する検索サービス

  • beanに一貫した名前を付け、実行時に名前で検索するだけです。

ユースケースは、アプリケーションの起動時にBeanが作成されるシナリオを中心に展開するようにも見えますが、選択したBeanを解決する必要があることに注意してくださいafterapplicationContext豆の注入が終了しました。

4
ninj