ランタイム値を指定してコンポーネント/サービスを注入する簡単な方法が見つかりません。
@ 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を使用して、ファクトリパターンのような実装を動的に挿入する方法
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);
}
}
_
これによればSO answer、using @Qualifier
を使用してもあまり役に立ちません: 修飾子でApplicationContextからBeanを取得します )
代替戦略として:
スプリングブートの場合は、 @ConditonalOnProperty
または別のConditional
を使用できます。
@aux
が示唆する検索サービス
ユースケースは、アプリケーションの起動時にBeanが作成されるシナリオを中心に展開するようにも見えますが、選択したBeanを解決する必要があることに注意してくださいafterapplicationContext豆の注入が終了しました。