为什么我需要一个no-args构造函数来在CDI中使用带有Constructor注入的ApplicationScoped bean?

为什么我需要一个no-args构造函数来在CDI中使用带有Constructor注入的ApplicationScoped bean?

问题描述:

我正在尝试将构造函数注入模式应用于我的CDI应用程序中的bean,并且遇到以下错误消息:

I'm trying to apply the constructor injection pattern to beans in my CDI application and am encountering the following error message:

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)

的确,为了使用构造函数注入模式,我有意设计了一个需要参数的构造函数的类:

Indeed, in order to use the constructor injection pattern, I have intentionally designed my class with a single constructor requiring arguments:

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

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

    ...
    ...

}

浏览 CDI不可提供的bean类型的规范,我看到:


3.15。不可提供的bean类型

容器使用代理提供某些功能。某些合法bean类型无法通过
代理容器:

The container uses proxies to provide certain functionality. Certain legal bean types cannot be proxied by the container:


  • 没有非私有构造函数但没有的类参数,

  • 声明为final的类,

  • 具有public,protected或
    默认可见性的非静态最终方法的类,

  • 原始类型,

  • 和数组类型。

  • classes which don’t have a non-private constructor with no parameters,
  • classes which are declared final,
  • classes which have non-static, final methods with public, protected or default visibility,
  • primitive types,
  • and array types.

如果注入点解析为
bean,则bean类型必须是可代理的:

A bean type must be proxyable if an injection point resolves to a bean:


  • 需要客户端代理,或者

  • 有一个关联装饰器,或者

  • 有一个绑定拦截器。

否则,容器会自动检测到问题,并将
视为部署问题。

Otherwise, the container automatically detects the problem, and treats it as a deployment problem.

正常范围和伪范围它声明:


所有正常范围必须显式声明@NormalScope,以向容器指示客户端代理是必需的。

All normal scopes must be explicitly declared @NormalScope, to indicate to the container that a client proxy is required.

给定 @ApplicationScoped bean根据定义 @NormalScope ,我需要一个非私有的无参数构造函数。那么我需要一个受保护的无参数构造函数来满足CDI规范吗?我尝试过使用受保护的无参数构造函数,它似乎有效,但我不明白WELD在这种情况下是如何工作的;在哪些条件下使用no-args构造函数?为什么这在CDI中是一个要求?

Given @ApplicationScoped beans are by definition @NormalScope, I need to have a non-private no-args constructor. So then I need to have a protected no-arg constructor just to satisfy the CDI spec? I've tried with a protected no-args constructor, and it seems to work, but I do not understand how WELD is working in that case; in which conditions does it use the no-args constructor? Why is this a requirement in CDI at all?

Weld是否只使用no-arg来创建代理,但在实际调用底层实现时,它使用了注入带参数的基于构造函数?

Does Weld only use the no-arg to create the proxy, but when actually calling the underlying implementation, it uses the inject-based constructor with arguments?

我会以更广泛的方式尝试回答,如果我错过了什么,请在下面告诉我。

I am going to try an answer it in a bit broader fashion, if I miss something, let me know below.

Weld需要做什么?

What does Weld need to do?

Weld需要的是实例化 @NormalScoped bean的代理。这样的代理不会携带太多信息,它或多或少只是一个代表它而不是上下文实例。代理将是一个扩展你的bean的类 - 这在任何地方都没有说明,但它是Weld(和OWB)的做法。如果你考虑一下它是有意义的......类型安全,拦截/装饰impl等等。它的行驶里程如何变化。 (它扩展bean的事实也是为什么有一个 protected no-args构造函数就足够了。进一步说明这就是为什么它必须调用一些构造函数的原因。超类)

What Weld needs is to instantiate a proxy of your @NormalScoped bean. Such proxy doesn't carry much information, it is more or less just a delegate which it hands around instead of the contextual instance. The proxy is going to be a class that extends your bean - this isn't stated anywhere, but it's how Weld (and OWB) does it. It makes sense if you think about it...type safety, interception/decoration impl and so on. The mileage of how it does this varies. (The fact that it extends the beans is also why having a protected no-args constructor will suffice. Furthermote it is a reason why it has to invoke some constructor of the superclass)

为什么有限制?

Why the limitation?

拥有no-arg构造函数的限制来自Java本身,其中以编程方式实例化对象的唯一合法方式是调用构造函数。 请注意,我们不是在讨论代理的实例化,而不是bean!调用参数化构造函数来创建代理实际上并不是一个选项,因为您没有关于参数应该具有什么值的上下文。你的bean可能有注入的构造函数( @Inject ),但是再次,代理不需要调用这样的构造函数,因为它们只是委托,它也可能会阻止一些循环注入的场景。此外,它还可能触发与其链接的其他对象的不期望的init。你只是不知道使用params在构造函数中可能会发生什么。

The limitation to have no-arg constructor comes from Java itself where the only legitimate way to programatically instantiate an object is to call a constructor. Please note that we are not talking instantiation of proxies, not beans! Invoking parameterized constructor to create a proxy is not really an option because you have no context as to what value should the parameters have. Your beans might have constructor with injection (@Inject) but then again, proxies need not call such constructors as they are just delegates and it would also possibly prevent some scenarios with circular injection. Furthermore it could also trigger undesired init of other objects linked to it. You just cannot know what might be happening inside constructor with params.

因此CDI规范要求你有no-args构造函数,这样Weld可以确定它总是存在并且可以用来安全地实例化它的代理而没有任何副作用。

Therefore CDI spec requires you to have no-args constructor so that Weld can be sure it is always there and can be used to safely instantiate it's proxy without any side-effects.

当你真的不能拥有no-arg构造函数时,可以节省生命/ em>

事实上,有一种解决方法。一个非便携式焊接配置选项,而不是使用构造函数可以使用不安全。如果您想知道如何,请参见文档启用它。

As a matter of fact, there is a way around this limitation. A non-portable Weld configuration option, which instead of using constructor can use Unsafe. See the docs if you wanna know how to enable it.