23

CDIを使用してパラメーターをメソッド呼び出しに挿入することは可能ですか?予想される動作は、フィールドインジェクションと同様です。優先生産者を検索し、製品を使用します。

私がしたいのはこれです:

public void foo(@Inject Bar bar){
  //do stuff
} 

またはこれ(より混乱の少ないsytaxで):

public void foo(){
  @Inject 
  Bar bar;
  //do stuff
} 

この構文はどちらの場合も違法です。代替手段はありますか?いいえの場合-可能であれば、これは何らかの理由で悪い考えですか?

ありがとうございました

編集-要件が十分に明確になっていない可能性があります-bar変​​数の初期化をコンテナに任せて、メソッドを直接呼び出すことができるようにしたいと思います。JörnHorstmannとPerceptionの答えは、それが不可能であることを示唆しています。

4

6 に答える 6

21

インジェクションポイントは、コンテナによってインスタンス化されるときにBeanに対して処理されます。これにより、メソッドレベルのインジェクションのユースケースの数が制限されます。仕様の現在のバージョンは、次のタイプのメソッド注入を認識します。

イニシャライザメソッドインジェクション

public class MyBean {
    private Processor processor;

    @Inject
    public void setProcessor(final Processor processor) {
        this.processor = processor;
    }
}

のインスタンスMyBeanが注入されると、setterメソッドを介してプロセッサインスタンスも注入されます。

イベントオブザーバーメソッド

public class MyEventHandler {
    public void processSomeEvent(@Observes final SomeEvent event) {
    }
}

イベントインスタンスは、イベント処理メソッドに直接注入されます(ただし、@ Injectアノテーションは使用されません)。

プロデューサーメソッド

public class ProcessorFactory {
    @Produces public Processor getProcessor(@Inject final Gateway gateway) {
        // ...
    }
}

プロデューサーメソッドへのパラメーターは自動的に注入されます。

于 2012-05-23T11:32:42.807 に答える
10

本当に必要なのがメソッドのパラメーター(呼び出し元によって提供される必要がある)ではなく、メソッドが呼び出されるたびに適切に初期化されたCDI Beanのインスタンスであり、完全に構築および注入されている場合は、チェックしてください。

javax.inject.Provider<T>

基本的に、最初にプロバイダーをクラスに注入します

@Inject Provider<YourBean> yourBeanProvider;

次に、メソッドで、新しいインスタンスを取得します

YourBean bean = yourBeanProvider.get();

お役に立てれば :)

于 2014-04-01T04:13:51.640 に答える
8

この質問は、私が最初にこのトピックを検索したときに出てきました。その後、CDI 1.1(JavaEE 7仕様に含まれる)のリリースにより、OPが部分的に望んでいたことを実際に実行する方法があることを学びました。あなたはまだできません

public void foo(@Inject Bar bar){
   //do stuff
}

ただし、ローカル変数を「注入」することはできますが、使用せず、@Injectプログラムで次のように挿入されたインスタンスを検索します。

public void foo() {
    Instance<Bar> instance = CDI.current().select(Bar.class);
    Bar bar = instance.get();
    CDI.current().destroy(instance);
    // do stuff with bar here
}

このselect()メソッドは、オプションで、提供する必要のある修飾子アノテーションを取得することに注意してください。しかし、インスタンスを取得して頑張っjava.lang.annotation.Annotationてください。Instance<Bar>必要なものを見つけるために、繰り返し処理する方が簡単な場合があります。

上記のように破棄する必要があると言われましたInstance<Bar>。経験から、上記のコードが機能することを確認できます。しかし、私はあなたがそれを破壊する必要があることを誓うことはできません。

于 2016-02-17T17:41:07.120 に答える
5

CDIのこの機能は、「初期化メソッド」と呼ばれます。構文は、メソッド全体に注釈が付けられているという点でコードとは異なります。@Injectメソッドパラメータには、特定のBeanを選択するための修飾子によってさらに注釈を付けることができます。JSR 299のセクション3.9は、次の例を示しています@Selected。Beanの実装が1つしかない場合は、省略できる修飾子を使用しています。

@Inject
void setProduct(@Selected Product product) {
    this.product = product;
}

その点に注意してください

アプリケーションは初期化メソッドを直接呼び出すことができますが、その場合、コンテナーによってメソッドにパラメーターが渡されることはありません。

于 2012-05-23T11:19:57.203 に答える
1

メソッドでBeanManagerAPIを使用してコンテキスト参照を取得するか、最終的な目標に応じて、

Instance<Bar>

メソッドの外で、メソッドで使用します。

于 2012-05-27T22:08:29.387 に答える
0

リフレクションを介してメソッドを呼び出すことが目標である場合は、InjectionPointパラメーターごとにを作成することができます。

CDI-SEを使用した例を次に示します。

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;

public class ParameterInjectionExample {

    public static class Foo {

        // this method will be called by reflection, all parameters will be resolved from the BeanManager
        // calling this method will require 2 different Bar instances (which will be destroyed at the end of the invocation)
        public void doSomething(Bar bar, Baz baz, Bar bar2) {
            System.out.println("got " + bar);
            System.out.println("got " + baz);
            System.out.println("got " + bar2);
        }
    }

    @Dependent
    public static class Bar {

        @PostConstruct
        public void postConstruct() {
            System.out.println("created " + this);
        }

        @PreDestroy
        public void preDestroy() {
            System.out.println("destroyed " + this);
        }
    }

    @ApplicationScoped
    public static class Baz {

        @PostConstruct
        public void postConstruct() {
            System.out.println("created " + this);
        }

        @PreDestroy
        public void preDestroy() {
            System.out.println("destroyed " + this);
        }
    }

    public static Object call(Object target, String methodName, BeanManager beanManager) throws Exception {
        AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(target.getClass());
        AnnotatedMethod<?> annotatedMethod = annotatedType.getMethods().stream()
                .filter(m -> m.getJavaMember().getName().equals(methodName))
                .findFirst() // we assume their is only one method with that name (no overloading)
                .orElseThrow(NoSuchMethodException::new);
        // this creationalContext will be valid for the duration of the method call (to prevent memory leaks for @Dependent beans)
        CreationalContext<?> creationalContext = beanManager.createCreationalContext(null);
        try {
            Object[] args = annotatedMethod.getParameters().stream()
                    .map(beanManager::createInjectionPoint)
                    .map(ip -> beanManager.getInjectableReference(ip, creationalContext))
                    .toArray();
            return annotatedMethod.getJavaMember().invoke(target, args);
        } finally {
            creationalContext.release();
        }
    }

    public static void main(String[] args) throws Exception {
        try (SeContainer container = SeContainerInitializer.newInstance().disableDiscovery().addBeanClasses(Bar.class, Baz.class).initialize()) {
            System.out.println("beanManager initialized");
            call(new Foo(), "doSomething", container.getBeanManager());
            System.out.println("closing beanManager");
        }
    }
}
于 2021-05-08T13:56:41.353 に答える