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

时间:2018-01-23 20:42:23

标签: java java-ee dependency-injection 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)

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

@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。不可提供的bean类型

     

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

     
      
  • 没有没有参数的非私有构造函数的类
  •   
  • 声明为final的类
  •   
  • 具有public,protected或者非静态最终方法的类   默认可见性,
  •   
  • 原始类型,
  •   
  • 和数组类型。
  •   
     

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

     
      
  • 需要客户端代理或
  •   
  • 有一个关联的装饰器,或
  •   
  • 具有绑定拦截器。
  •   
     

否则,容器会自动检测问题并进行处理   它是一个部署问题。

Normal scopes and pseudo-scopes部分进一步说明:

  

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

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

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

2 个答案:

答案 0 :(得分:6)

如果我想念一些东西,我会尝试以更广泛的方式回答它,请在下面告诉我。

Weld需要做什么?

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

为什么有限制?

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

因此,CDI规范要求您使用no-args构造函数,以便Weld可以确保它始终存在并且可以用于安全地实例化它的代理而没有任何副作用。

当你真正无法使用无法构造函数时,可以节省生命

事实上,有一种方法可以解决这个问题。一个非便携式焊接配置选项,而不是使用构造函数可以使用Unsafe。如果您想知道如何启用它,请参阅the docs

答案 1 :(得分:2)

  

我需要一个受保护的无参数构造函数才能满足CDI规范?   在哪些条件下使用no-args构造函数?为什么这是CDI的要求?

就像你引用的那样,在CDI规范中,如果bean没有arg构造函数但是有args构造函数,那么bean将变得无法解释。它不是&#34;只是为了规范&#34;虽然在需求服务器没有意义的意义上:CDI使用的代理创建机制需要这个。他们首先创建代理,然后是实现。

  

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

简而言之,是的。

我在类似方案中使用的另一种替代方法是@ApplicationScoped假镜,而不是@Singleton。这没有no-param构造函数,因为它没有使用普通范围。这意味着虽然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资源是可选的。   便携式应用程序必须改为使用字段或bean属性   与@PostConstruct注释方法结合使用。实现   应该警告用户使用非便携式构造函数注入。

因此可能会或可能不会起作用,具体取决于实施情况。我使用Weld为我的班级工作。)