我正在尝试将构造函数注入(inject)模式应用于我的 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)
事实上,为了使用构造函数注入(inject)模式,我特意将我的类设计为具有需要参数的单个构造函数:
@ApplicationScoped
@Typed(LoginManagerAction.class)
public class LoginManagerAction extends UtilBasicDispatchAction {
@Inject
public LoginManagerAction( SessionManager sessionManager, JMSHealthCheckService jmsHealthCheckService) {
super();
this.sessionManager = sessionManager;
this.jmsHealthCheckService = jmsHealthCheckService;
}
...
...
}
浏览CDI Specs of Unproxyable bean types ,我看到了:
3.15. Unproxyable bean types
The container uses proxies to provide certain functionality. Certain legal bean types cannot be proxied by the container:
- 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.
A bean type must be proxyable if an injection point resolves to a bean:
- that requires a client proxy, or
- that has an associated decorator, or
- that has a bound interceptor.
Otherwise, the container automatically detects the problem, and treats it as a deployment problem.
并在 Normal scopes and pseudo-scopes 部分进一步介绍它指出:
All normal scopes must be explicitly declared @NormalScope, to indicate to the container that a client proxy is required.
根据 @NormalScope
的定义,鉴于 @ApplicationScoped
beans,我需要一个非私有(private)的无参数构造函数。那么我需要一个 protected 无参数构造函数来满足 CDI 规范吗?我试过使用 protected 无参数构造函数,它似乎可以工作,但我不明白 WELD 在这种情况下是如何工作的;它在什么情况下使用无参数构造函数?为什么这是 CDI 中的一项要求?
Weld 是否仅使用无参数创建代理,但在实际调用底层实现时,它使用带参数的基于注入(inject)的构造函数?
最佳答案
我将尝试以更广泛的方式回答它,如果我遗漏了什么,请在下面告诉我。
Weld 需要做什么?
Weld 需要的是实例化您的 @NormalScoped
bean 的代理。这样的代理不携带太多信息,它或多或少只是一个委托(delegate),而不是上下文实例。代理将是一个扩展您的 bean 的类——这在任何地方都没有说明,但 Weld(和 OWB)就是这样做的。如果您考虑一下,这是有道理的……类型安全、拦截/装饰 impl 等等。它如何做到这一点的里程各不相同。 (因为它扩展了 bean,所以有一个 protected
无参数构造函数就足够了。它必须调用父类(super class)的一些构造函数)
为什么会有限制?
具有无参数构造函数的限制来自 Java 本身,其中以编程方式实例化对象的唯一合法方法是调用构造函数。 请注意,我们不是在谈论代理的实例化,不是 beans!调用参数化构造函数来创建代理并不是一个真正的选择,因为您不知道参数应该是什么。
bean 可能有一个带注入(inject)的构造函数 (@Inject
)
但是代理需要创建一个无参数的构造函数。
它也可能会阻止一些循环注入(inject)的情况。 此外,它还可能触发链接到它的其他对象的意外初始化。 您只是无法知道带参数的构造函数内部会发生什么。
因此,CDI 规范要求您拥有无参数构造函数,以便 Weld 可以确保它始终存在,并且可以用于安全地实例化它的代理而没有任何副作用。
当您确实无法使用无参数构造函数时的救命稻草
事实上,有一种方法可以绕过这个限制。一个不可移植的 Weld 配置选项,它可以使用 Unsafe
而不是使用构造函数。参见 the docs如果您想知道如何启用它。
关于java - 为什么我需要一个无参数构造函数来在 CDI 中使用 ApplicationScoped bean 和构造函数注入(inject)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48410451/