2017-12-12 18 views
1

私はこのことについて考えているかどうかはわかりませんが、私はSpringでのものに似た何かを探しています - @ConditionalOnMissingBean - tell spring - 指定されたbeanが見つからない場合にのみ作成します。同じタイプの別のCDIが存在する場合は、CDI Beanにアクセスしてください。

私は拡張機能を使ってみましたが、いくつかのイベントをタップしてVETO Beanに使用できるように見えました。 1つの方法は、この段階でBeanManagerを持っていて、既に存在するBeanを探して、もしあなたが挿入しようとしているBeanを含んでいれば、これをVETOします。しかし、これはすべての豆を見たときにのみ機能します。

しかし、AfterBeanDiscoveryが呼び出される前に、適切なように見えます。検証は失敗し、同じタイプの複数のBeanに不満があります。

私はここでいくつかの助けを得ることができれば素晴らしいだろう。

+0

私は何を探していることは、我々は 'Alternative' CDIに呼んでと考えています。しかし、私は少し良く理解したい。あなたはインターフェース 'Interface1'を持っています。 Beanとして登録されている他の実装がない場合にのみ、このインタフェースの実装をインストールしますか? –

+0

はい、私はこのコンテキストに、その "あいまいな依存関係"につながる同じ種類の複数の豆を持たせたくありません。私は同じ種類の他のBeanがすでにコンテキスト内に存在しない場合にのみ、1つを生成したいと思います。存在していれば、そのプロデューサーは蹴らないでください。 – karansardana

答えて

0

あなたの質問は面白くて、で、CDI拡張機能を使って解くことができます。それは扱いにくいので素朴です。プロデューサのメソッド/フィールドがあり、それ以上見つからない可能性があります。

CDI拡張機能は本当に素晴らしく、強力ですが、むしろ技術的なことがありますので、最初に他の方法を検討してみましょう。

  1. 専門:

    @Specializes 
    public class SomeServiceSpecialImpl extends SomeServiceDefaultImpl {...} 
    
    多分あなたのユースケースは、あなたが開発者が行う必要がありますpublic class SomeServiceDefaultImpl、たとえば、通って、それを上書きするために、SomeServiceのデフォルトの実装を提供することを明示的に文書化するために十分です

    John Amentのコメントに記載されているように、代替案も検討してください。

  2. 修飾子:このサービスは一つだけの場所/いくつかの場所で使用され、唯一のあなたのコード内で、カスタム修飾子を使用して、あなたのSomeServiceDefaultImplを修飾することができれば、@MyDefaultImplを言います。その後、Instance<SomeService>を注入最初の修飾されていないインスタンスを探し、それが満たされない場合は、資格を探して - の線に沿って何か:強制するように

    private SomeService someService; 
    
    @Inject 
    void setSomeServiceInstance(Instance<SomeService> s) { 
        // not tried, please adapt as needed 
        if(s.isUnsatisfied()) { 
         someService = s.select(new MyDefaultImplAnnotation()).get(); 
        } 
        else { 
         someService = s.get(); 
        } 
    } 
    
  3. @Vetoedあるデフォルトの実装を提供します実装を提供するコードのクライアント。クライアントがデフォルトを使用する場合、単にプロデューサを使用することができます。


以下の実装がコンセプトの証明である、上記の言った:

  • は、デフォルトの実装上に存在することが、次の注釈が必要です:

    @Target({ TYPE, METHOD, FIELD }) 
    @Retention(RUNTIME) 
    @Documented 
    public @interface ConditionalOnMissingBean { 
        Class<?> value(); 
    } 
    

    value()は必須で、「デフォルト」のBeanタイプを示します。あなたの実装はよりスマートになります。すなわち、実際のデフォルトの実装からBeanの型を検出しますが、それは概念の証明に過ぎません!

  • 生産者を大いに無視します!

  • 軽くテストされているので、おそらく邪悪なコーナーケースがあるので、注意してください!あなたが拡張(META-INF /サービス/ javax.enterprise.inject.spi.Extension、beans.xmlの)すべての振り付けを必要とするコードに加えて

import java.util.HashMap; 
import java.util.Map; 

import javax.enterprise.event.Observes; 
import javax.enterprise.inject.spi.AfterBeanDiscovery; 
import javax.enterprise.inject.spi.AnnotatedType; 
import javax.enterprise.inject.spi.Bean; 
import javax.enterprise.inject.spi.BeanAttributes; 
import javax.enterprise.inject.spi.BeanManager; 
import javax.enterprise.inject.spi.Extension; 
import javax.enterprise.inject.spi.InjectionTargetFactory; 
import javax.enterprise.inject.spi.ProcessAnnotatedType; 

public class ConditionalOnMissingBeanExtension implements Extension { 

    private Map<Class<?>, AnnotatedType<?>> map = new HashMap<>(); 

    <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) { 
     AnnotatedType<?> annotatedType = pat.getAnnotatedType(); 
     ConditionalOnMissingBean annotation = annotatedType.getAnnotation(ConditionalOnMissingBean.class); 
     if(annotation != null) { 
      map.put(annotation.value(), annotatedType); 
      pat.veto(); 
     } 
    } 

    void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager beanManager) { 
     map.entrySet().stream() 
      .filter(e -> doesNotHaveBeanOfType(beanManager, e.getKey())) 
      .map(e -> defineBean(beanManager, e.getValue())) 
      .forEach(abd::addBean); 
     map = null; 
    } 

    private boolean doesNotHaveBeanOfType(BeanManager beanManager, Class<?> type) { 
     return beanManager.getBeans(type).isEmpty(); 
    } 

    private <T> Bean<T> defineBean(BeanManager beanManager, AnnotatedType<T> annotatedType) { 
     BeanAttributes<T> beanAttributes = beanManager.createBeanAttributes(annotatedType); 
     InjectionTargetFactory<T> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType); 
     return beanManager.createBean(beanAttributes, annotatedType.getJavaClass(), injectionTargetFactory); 
    } 
} 

サービス・インターフェースのデフォルトの実装の例は次のようになります。

@ApplicationScoped 
@ConditionalOnMissingBean(SomeService.class) 
public class SomeServiceDefaultImpl implements SomeService { 

    @Override 
    public String doSomeCalculation() { 
     return "from default implementation"; 
    } 
} 
+0

このNikosのおかげで、私はこの種のものも試してみましたが、「条件付き」注釈付きのものを拒否しなかったと思います。その検証エラーがafterBeanDiscoveryフェーズに達する前に確認してください。私の部分で間違った間違い、多くのありがとう、ニコ。 – karansardana

+0

しかし、特殊化と修飾子は、私が望むものとは正確には動作できません。同じ種類のものがすでに存在する場合、コンテキスト内に特殊化されたBeanは必要ありません。 @Specializationを使って、私は既存のものを上書きするつもりです - 私はこのケースを提供しません - 他の[私のコントロールがライブラリではありません]がないときだけ私のものが存在することを望みます。 – karansardana

関連する問題