2017-03-17 9 views
1

私は、実行時にSpringアプリケーションによって検出された環境に応じて、サービスの3つの可能な実装の1つを条件付きで作成する必要があります。サービスAが利用可能な場合、サービスAを依存関係として使用する具体的な実装クラスを作成します。サービスAが利用できない場合は、依存関係としてサービスBを使用して実装を作成します。等々。サービスの可用性に基づく春のオートワイヤリング

実装に依存するクラスは、インターフェイスの自動配線を行い、特定の環境で選択されたサービスが何であるかには関係ありません。

この最初のスタブは、サービスが利用可能かどうかに応じてBeanまたはnullを返す複数の@Beanメソッドを実装し、@Autowire(required = false)という個別の@Configurationクラスを持つことでした。 @Autowiredフィールドのどれがnullでないかによって条件付きで実装を作成する2つの可能なサービス。

ここで問題となるのは、required = falseの場合、Springは候補の生成を待つかどうかを気にするようには見えません。つまり、実装を選択しようとするクラスは、required = false Beanのいずれかまたは両方が構築される前に構築されるため、正しく初期化できるかどうかにかかわらず、一方または両方が常にnullになることがあります。

この時点で私は穀粒に逆らっているような気がするので、この種のことを行うための「正しい」方法についてのアドバイスを探しています。いくつかの外部サービスまたは環境の可用性に基づいています。

プロファイルは正しい答えのようには見えません。サービス豆が選択したい実装を初期化しようとするまでわからないからです。私は文脈を作る時にそれを知りません。

@Orderでも目標は達成されません。 @BonditionalとBeanの存在についてのテスト(まだまだ構築されていない可能性があるので)はありません。 FactoryBeanにも同じ問題があります.FactoryBeanがインスタンスの作成を要求された時点で構築されていない可能性のあるBeanの有無をチェックするのは良いことではありません。

私が本当に必要とするのは、他のBeanの可用性に基づいてBeanを作成することですが、そのBeanが少なくとも初期化を試みた後にのみ作成してください。

+0

3つの実装が何であるかについてもう少し言えますか? –

+0

具体的な例を挙げると、可用性に応じて、外部のRedisキャッシュ、メモリ内のクラスタ化されたHazelcastキャッシュ、またはどちらも使用できない場合は、ダムのローカルHashMap(フォールバック)があります。 私は何を持っているかに応じて、利用可能なサービスが提供する特定のBeanを取得/配置する方法を知っているServiceインターフェイスの具体的な実装を構築します。 場合(Redisの豆)RedisImpl(Redisの豆を)返す 他の場合(hazelcast豆)HazelImpl(hazelcast豆)を返し、他の DumbHashImpl() –

+0

は、だから私はやりたいかもしれないもののようなものです。明確にするために、環境に関連する特定のBeanの可用性がありますか?たとえば、純粋にdevでDumbHashImpl()を使用し、テストでRedisImplを使用し、prodでHazelImplを使用していますか? –

答えて

0

春のプロフィールはあなたの友人です。現在のプロファイルは、環境変数、コマンドライン引数、およびその他の方法で設定できます。 Spring管理コンポーネントにアノテーションを付けて、特定のプロファイル用に作成することができます。この場合、まあ

Spring Profiles from the Spring Documentation

+0

私のアプリケーションがいつ私が必要とするプロファイルを開始するのかわからないとき、プロファイルは答えではありません。 –

+0

@NickJohnson十分ですが、役立つならば、少なくともSpring Bootでは、実行時にプロファイルをプログラムで設定することもできます。 – RichW

+0

確かに。この場合の問題は、鶏と卵の一種です。私は既に豆を初期化しようとするまで、どのプロファイルを選択するのかわかりません...つまり、私はどのプロファイルを選択するかをすでに知っておく必要がありました。 –

0

それは全体の間違った行動に影響を与えた接線間違いであることが判明しました。ここ@Autowiredを取得豆が原因不足している構成に(利用できない場合、他の@Configurationクラスで

@Autowired(required=false) 
@Qualifier(RedisConfig.HISTORY) 
private RLocalCachedMap<String, History> redisHistoryMap; 

@Autowired(required=false) 
@Qualifier(HazelcastConfig.HISTORY) 
private IMap<String, History> hazelcastHistoryMap; 

// RequestHistory is an interface 
@Bean 
public RequestHistory requestHistory() { 
    if (redisHistoryMap != null) { 
     return new RedisClusteredHistory(redisHistoryMap); 
    } else if (hazelcastHistoryMap != null) { 
     return new HazelcastClusteredHistory(hazelcastHistoryMap); 
    } else { 
     return new LocalRequestHistory(); // dumb hashmap 
    } 
} 

:いくつかの背景を与えること

は、私の、最初の素朴な(しかし、実行可能な)アプローチは、このように見えました、初期化時の例外など)、それらを作成する@Beanメソッドはnullを返します。

の挙動を観察したがRLocalCachedMap<>@Beanメソッドを呼び出しますが、前に、春はその@Beanメソッドを呼び出すことによってIMap<>を作成しようとしてしまった後に、この@Beanメソッドが呼び出さなっていたということでした。私は間違ってこれはrequired=falseと関係があると思っていましたが、実際にはそれは関係ありませんでした。実際に何が起こった

は、この@Configurationクラスのためにその依存関係のグラフを計算したとき、そのおそらく春は違いを見分けることができませんでした...私は誤って両方@Bean名(その結果、@Qualifier秒)のために同じ定数を使用したため、 2つの@Autowire豆は同じものであるように見えました(同じ名前だったので)。

(この場合、私はここに入るが、それはそれは同じタイプの多くのマップを持つことが可能だと言うには十分ではないだろうに@Qualifierを使用するための二次的な理由があります。)

私は資格たら名前はよりよく、コードはちょうど私がそれがほしいと思ったものでした。しかし、やや面白くない/醜いです。

ある時点で、私は戻って、よりエレガントに見えるか、醜いかどうかを確認し、if/else汚れの代わりに@Conditional@Primaryを使用するだけで動作します。

ここでの教訓は、Beanに明示的に名前を付ける場合、このようなものを交換する予定がある場合でも、アプリケーション全体で固有の名前がであることを確かめてください。

関連する問題