Eri*_* B. 9 java dependency-injection java-ee cdi weld
我正在尝试将构造函数注入模式应用于我的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)
Run Code Online (Sandbox Code Playgroud)
实际上,为了使用构造函数注入模式,我有意设计了一个带有一个需要参数的构造函数的类:
@ApplicationScoped
@Typed(LoginManagerAction.class)
public class LoginManagerAction extends UtilBasicDispatchAction {
@Inject
public LoginManagerAction( SessionManager sessionManager, JMSHealthCheckService jmsHealthCheckService) {
super();
this.sessionManager = sessionManager;
this.jmsHealthCheckService = jmsHealthCheckService;
}
...
...
}
Run Code Online (Sandbox Code Playgroud)
通过CDI Specs of Unproxyable bean类型,我看到:
3.15.不可提供的bean类型
容器使用代理来提供某些功能.某些合法bean类型无法由容器代理:
- 没有没有参数的非私有构造函数的类,
- 宣布为最终的类,
- 具有公共,受保护或默认可见性的非静态最终方法的类,
- 原始类型,
- 和数组类型.
如果注入点解析为bean,则bean类型必须是可代理的:
- 这需要客户端代理,或
- 有一个相关的装饰,或
- 有一个绑定的拦截器.
否则,容器会自动检测问题,并将其视为部署问题.
在下面的Normal范围和伪范围部分中,它指出:
必须显式声明所有正常范围@NormalScope,以向容器指示需要客户端代理.
根据@ApplicationScoped定义@NormalScope,给定bean ,我需要一个非私有的无参数构造函数.那么我需要一个受保护的无参数构造函数来满足CDI规范吗?我尝试过使用受保护的无参数构造函数,它似乎有效,但我不明白WELD在这种情况下是如何工作的; 在哪些条件下使用no-args构造函数?为什么这是CDI的要求呢?
Weld是否仅使用no-arg来创建代理,但在实际调用底层实现时,它使用带有参数的基于注入的构造函数?
我需要一个受保护的无参数构造函数来满足 CDI 规范吗?它在哪些条件下使用无参数构造函数?为什么这在 CDI 中是一项要求?
就像您引用的那样,在 CDI 规范中,如果 bean 没有无参数构造函数但具有带有 args 的构造函数,则它们将变得不可代理。这不是“仅用于规范”,尽管从某种意义上说,要求没有任何用处:CDI 使用的代理创建机制需要这个。他们首先创建代理,然后是实现。
Weld 是否只使用 no-arg 来创建代理,但在实际调用底层实现时,它使用带参数的基于注入的构造函数?
简而言之,是的。
我在类似场景中使用的一种替代方法是@Singleton伪镜,而不是@ApplicationScoped。这确实可以在没有无参数构造函数的情况下工作,因为它不使用正常范围。这意味着虽然 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));
}
}
Run Code Online (Sandbox Code Playgroud)
(但请注意,如果您像我一样将它们用于 jax-rs 资源,jax-rs 规范是这样说的:
对 JAX-RS 资源的构造函数注入的支持是可选的。便携式应用程序必须将字段或 bean 属性与 @PostConstruct 注释方法结合使用。实现应该警告用户使用不可移植的构造函数注入。
所以它可能会也可能不会起作用,这取决于实现。我在课堂上使用了 Weld。)
我会以更广泛的方式尝试回答它,如果我错过了什么,请在下面告诉我.
Weld需要做什么?
Weld需要的是实例化@NormalScopedbean 的代理.这样的代理不会携带太多信息,它或多或少只是它所代表的代理而不是上下文实例.代理将成为一个扩展你的bean的类 - 这在任何地方都没有说明,但这就是Weld(和OWB)的做法.如果你考虑一下它是有意义的......类型安全,拦截/装饰impl等等.它的行驶里程如何变化.(它扩展bean的事实也是为什么有一个protectedno-args构造函数就足够了.进一步说明它是为什么它必须调用超类的一些构造函数的原因)
为什么有限制?
拥有no-arg构造函数的限制来自Java本身,其中以编程方式实例化对象的唯一合法方式是调用构造函数.请注意,我们不是在谈论代理的实例化,而不是bean!调用参数化构造函数来创建代理实际上不是一个选项,因为您没有关于参数应该具有什么值的上下文.你的bean可能有带有inject(@Inject)的构造函数,但是再次,代理不需要调用这样的构造函数,因为它们只是委托,它也可能会阻止某些循环注入的场景.此外,它还可能触发与其链接的其他对象的不期望的init.你只是无法知道使用params在构造函数中发生了什么.
因此,CDI规范要求您使用no-args构造函数,以便Weld可以确保它始终存在并且可以用于安全地实例化它的代理而没有任何副作用.
当你真正拥有无参数构造函数时,可以节省生命
事实上,有一种方法可以解决这个问题.一个非便携式焊接配置选项,而不是使用构造函数可以使用Unsafe.如果您想知道如何启用它,请参阅文档.