web-dev-qa-db-ja.com

CDI内でコンストラクターインジェクションでApplicationScopedBeanを使用するために、引数なしのコンストラクターが必要なのはなぜですか?

CDIアプリケーションのBeanにコンストラクター注入パターンを適用しようとしていますが、次のエラーメッセージが表示されます。

15:18:11,852 ERROR [izone.adams.webapp.error.IzoneExceptionHandler] (default task-40) org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435: Normal scoped bean class webapp.util.LoginManagerAction is not proxyable because it has no no-args constructor - <unknown javax.enterprise.inject.spi.Bean instance>.
        at org.jboss.weld.bean.proxy.DefaultProxyInstantiator.validateNoargConstructor(DefaultProxyInstantiator.Java:50)

実際、コンストラクター注入パターンを使用するために、引数を必要とする単一のコンストラクターを使用してクラスを意図的に設計しました。

@ApplicationScoped
@Typed(LoginManagerAction.class)
public class LoginManagerAction extends UtilBasicDispatchAction {

  @Inject
   public LoginManagerAction( SessionManager sessionManager, JMSHealthCheckService jmsHealthCheckService) {
       super();
       this.sessionManager = sessionManager;
       this.jmsHealthCheckService = jmsHealthCheckService;
   }

    ...
    ...

}

プロキシ不可能なBeanタイプのCDI仕様 を見ると、次のことがわかります。

3.15。プロキシ不可能なBeanタイプ

コンテナはプロキシを使用して特定の機能を提供します。特定の正当なBeanタイプは、コンテナーによってプロキシできません。

  • パラメータのない非プライベートコンストラクタを持たないクラス、
  • 最終的に宣言されたクラス、
  • パブリック、保護、またはデフォルトの可視性を備えた非静的なfinalメソッドを持つクラス
  • プリミティブ型、
  • および配列型。

インジェクションポイントがBeanに解決される場合、Beanタイプはプロキシ可能である必要があります。

  • クライアントプロキシが必要、または
  • デコレータが関連付けられている、または
  • バインドされたインターセプターがあります。

それ以外の場合、コンテナーは問題を自動的に検出し、デプロイメントの問題として扱います。

そしてさらにセクション 通常のスコープと疑似スコープ それは述べています:

クライアントプロキシが必要であることをコンテナに示すために、すべての通常のスコープは明示的に@NormalScopeとして宣言する必要があります。

与えられた@ApplicationScopedBeanは定義上@NormalScope、非プライベートの引数なしコンストラクターが必要です。では、CDI仕様を満たすためだけに、保護された引数なしのコンストラクターが必要ですか?保護された引数なしのコンストラクターを試してみましたが、機能しているようですが、その場合にWELDがどのように機能しているかわかりません。どの条件で引数なしコンストラクターを使用しますか?これがCDIの要件であるのはなぜですか?

Weldはno-argを使用してプロキシを作成しますが、実際に基になる実装を呼び出すときは、引数付きの注入ベースのコンストラクターを使用しますか?

9
Eric B.

私はもう少し広い方法で答えを試みるつもりです、私が何かを逃したならば、私に以下に知らせてください。

Weldは何をする必要がありますか?

Weldに必要なのは、@NormalScopedBeanのプロキシをインスタンス化することです。このようなプロキシは多くの情報を伝達しません。コンテキストインスタンスではなく、多かれ少なかれ単なるデリゲートです。プロキシは、Beanを拡張するクラスになります。これはどこにも記載されていませんが、Weld(およびOWB)がそれを行う方法です。あなたがそれについて考えるならば、それは理にかなっています...タイプ安全性、傍受/装飾implなど。これをどのように行うかによって、走行距離は異なります。 (Beanを拡張するという事実は、protected no-argsコンストラクターで十分な理由でもあります。さらに、スーパークラスのコンストラクターを呼び出さなければならない理由です)

なぜ制限があるのですか?

引数なしのコンストラクターを持つという制限は、Java自体から生じます。ここで、オブジェクトをプログラムでインスタンス化する唯一の正当な方法は、コンストラクターを呼び出すことです。私たちはそうではないことに注意してくださいBeanではなくプロキシのインスタンス化について話します!パラメータが持つべき値に関するコンテキストがないため、パラメータ化されたコンストラクタを呼び出してプロキシを作成することは実際にはオプションではありません。Beanにはインジェクション付きのコンストラクタがある可能性があります(@Inject)しかし、プロキシは単なるデリゲートであるため、そのようなコンストラクタを呼び出す必要はなく、循環インジェクションを伴う一部のシナリオを妨げる可能性もあります。さらに、それにリンクされている他のオブジェクトの望ましくない初期化をトリガーする可能性もあります。コンストラクター内でパラメーターを使用して発生します。

したがって、CDI仕様では、引数なしのコンストラクターが必要です。これにより、Weldは常に存在し、副作用なしにプロキシを安全にインスタンス化するために使用できます。

引数なしのコンストラクターを本当に持つことができない場合の命の恩人

実際のところ、この制限を回避する方法があります。コンストラクターを使用する代わりにUnsafeを使用できる移植性のないWeld構成オプション。有効にする方法を知りたい場合は、 ドキュメント を参照してください。

6
Siliarus

CDI仕様を満たすためだけに、保護された引数なしのコンストラクターが必要ですか?どの条件で引数なしコンストラクターを使用しますか?これがCDIの要件であるのはなぜですか?

引用したように、CDI仕様では、Beanに引数なしのコンストラクターがなく、引数付きのコンストラクターがある場合、Beanはプロキシ不可になります。要件サーバーには目的がないという意味では、「仕様のためだけ」ではありません。CDIで使用されるプロキシ作成メカニズムにはこれが必要です。最初にプロキシを作成し、次に実装を作成します。

Weldはno-argを使用してプロキシを作成しますが、実際に基になる実装を呼び出すときは、引数付きの注入ベースのコンストラクターを使用しますか?

要するに、はい。

@ ApplicationScoped の代わりに、同様のシナリオで使用した1つの代替手段は、 @ Singleton 疑似スコープです。通常のスコープを使用していないため、パラメーターなしのコンストラクターがなくても機能します。これは、Beanがプロキシされないことを意味します。私のユースケースでは、これは問題ありませんでした。クラスの例を次に示します。

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/add")
@Singleton
public class CounterController {

    private CounterService counterService;

    @Inject
    public CounterController(@Context CounterService counterService) {
        this.counterService = counterService;
    }

    @POST
    public void add(@Suspended final AsyncResponse asyncResponse, @Valid
            CounterRequest counterRequest) {
        asyncResponse.resume(counterService.count(counterRequest));
    }
}

(ただし、私が持っているようにjax-rsリソースにそれらを使用する場合、jax-rs仕様は次のように述べていることに注意してください。

JAX-RSリソースのコンストラクターインジェクションのサポートはオプションです。ポータブルアプリケーションは、代わりに@PostConstructアノテーション付きメソッドと組み合わせてフィールドまたはBeanプロパティを使用する必要があります。実装は、ポータブルでないコンストラクタインジェクションの使用についてユーザーに警告する必要があります。

したがって、実装に応じて、機能する場合と機能しない場合があります。私はそれが機能するクラスにWeldを使用しました。)

2
eis