web-dev-qa-db-ja.com

挿入されたコンストラクターでプロキシできないCDIオブジェクト

CDI Bean(ApplicationScoped)のコンストラクターに引数を挿入しようとすると、次の問題が発生します。

Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435: Normal scoped bean class xx.Config is not proxyable because it has no no-args constructor - Managed Bean [class xx.Config] with qualifiers [@Default @Named @Any].
    at org.jboss.weld.bean.proxy.DefaultProxyInstantiator.validateNoargConstructor(DefaultProxyInstantiator.Java:50)
    at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.Java:217)
    at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.Java:178)

ただし、クラスには注入可能なコンストラクターがあります。

@Inject
public Config(ConfigLocator configLocator) {
    defaultConfigPath = configLocator.getPath();
    doStuff();
}

デフォルトのコンストラクター、変数インジェクション、およびpostconstructメソッドを使用すると、これはすべて正常に機能しますが、この場合はコンストラクターインジェクションをお勧めします。

ここで何がうまくいかないのか考えはありますか?

12

クラスをインターフェースと実装に分割する同様の問題を解決しました。あなたの場合、次のようなものです。

public interface Config
{
  // API here
}

@ApplicationScoped @Priority(0)
public class ConfigImpl implements Config
{
  @Inject
  public ConfigImpl(ConfigLocator configLocator) { ... }

  // API implementation here
}

この例はあなたを助けるかもしれません:

@ApplicationScoped
public class Config {

    private String defaultConfigPath;  

    @Inject
    public Config(ConfigLocator configLocator) {
       this.defaultConfigPath = configLocator.getPath();
       doStuff();
    }

    // create a no-args constructor which is required for any scoped bean.
    public Config() {
    }

}

@ApplicationScoped Beanにパブリック非引数コンストラクターが必要です。

注:このクラスのBeanは一度だけ作成され、アプリケーションの存続期間全体にわたって維持されます。このBeanはすべての管理対象Bean間で共有されます。@ ApplicationScopedBeanは本質的にシングルトンです。

言及された問題:

Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435: Normal scoped bean class xx.Config is not proxyable because it has no no-args constructor - Managed Bean [class xx.Config] with qualifiers [@Default @Named @Any].

これの考えられる理由は、実行時に目的のプロキシBeanを取得できるように、CDIのパブリックno-argsコンストラクターを提供するために非依存スコープBeanが必要であるためです。

1
Anish B.

実装がマネージドBeanへのプロキシを作成するには、非プライベートの引数なしのコンストラクターが必要です。非プライベートの引数なしのコンストラクターの存在に基づいて、注入されたコンストラクターの機能が失われることはありません。

コンテナはプロキシを使用して、インターセプトや装飾などを許可し、Beanが逆参照されたときに適切なコンテキストインスタンスを取得します。 Bean間の循環注入を可能にすることも必要です。

1
covener

ウラジミールはあなたを正しい方向に向けていると思います。

私はCDI(およびWeld)を学習しているだけなので、私の答えはすべての面で100%正確ではないかもしれませんが、引数なしのコンストラクターを持つ注入ポイントで型を選択する必要があるようです。

今日、次のタイプ階層で同じエラーが発生しました。

  • 提供されているIServerInterceptorインターフェースがあります。
  • 依存関係をとるコンストラクターが1つあるカスタム実装AuthenticationInterceptorがあります(面白い事実:これは(C)DIについて何も知りません)
  • コンストラクターが1つだけ注入されたInjectableAuthenticationInterceptorがあります。

AuthenticationInterceptorのインスタンスを注入したい別のBeanがあります。フィールドをタイプIServerInterceptorとして定義すると機能しますが(インターフェイスであり、weldはそのプロキシを作成できるため(?))、フィールドをAuthenticationInterceptorとして定義すると機能しなくなります。

いくつかのコードで:

_// IServerInterceptor.Java
public interface IServerInterceptor {
}

// AuthenticationInterceptor.Java
public class AuthenticationInterceptor extends InterceptorAdapter {
    private final Predicate<String> validAccessTokenString;
    private final Function<String, AccessToken> toAccessTokenModel;
    private final LoginManager<AccessToken> loginManager;

    public AuthenticationInterceptor(Predicate<String> validAccessTokenString, Function<String, AccessToken> toAccessTokenModel, LoginManager<AccessToken> loginManager) {
        this.validAccessTokenString = validAccessTokenString;
        this.toAccessTokenModel = toAccessTokenModel;
        this.loginManager = loginManager;
    }
    // ...
}


// InjectableAuthenticationInterceptor.Java
@ApplicationScoped
public class InjectableAuthenticationInterceptor extends AuthenticationInterceptor {
    @Inject
    public InjectableAuthenticationInterceptor(LoginManager<AccessToken> loginManager) {
        super(isWelformedAccessToken(), toAccessToken(), loginManager);
    }
}
_

さて、

_@Inject private IServerInterceptor authenticationInterceptor;
_

うまく機能しますが

_@Inject private AuthenticationInterceptor authenticationInterceptor;
_

ではない。

0
uthomas

Splittinginterfaceと実装では、問題を完全に解決することはできません。問題は、interfaceBeanがプロキシ可能であるためにデフォルトの"no args" Construcutorを必要とするため、ApplicationScopedApplicationScopedにならないことです。このように、常に実装の新しいインスタンスを作成します。したがって、実装は@Dependentアノテーション付きBeanのように動作します。

この方法で解決したい場合は、@PostConstructのメソッドを使用してargumentの挿入を処理し、non arg constructorのみを使用する必要があります。

0
Manuel Muggli