Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need a no-args constructor to use ApplicationScoped beans with Constructor injection within CDI?

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;
   }

    ...
    ...

}

Looking through the CDI Specs of Unproxyable bean types, I see that:

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.

And in further in section Normal scopes and pseudo-scopes it states:

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

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?

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?

like image 551
Eric B. Avatar asked Jan 23 '18 20:01

Eric B.


People also ask

What is the purpose for the no arg constructor?

In Java, a no-argument constructor is the default constructor and if you don't define explicitly in your program. Then Java Compiler will create a default constructor with no arguments. The purpose is to call the superclass constructor.

How do you inject a Quarkus?

5.3. In CDI, if you declare a field injection point you need to use @Inject and optionally a set of qualifiers. @Inject @ConfigProperty(name = "cool") String coolProperty; In Quarkus, you can skip the @Inject annotation completely if the injected field declares at least one qualifier.

What is not Proxyable because it has no no args constructor?

Config is not proxyable because it has no no-args constructor - Managed Bean [class xx. Config] with qualifiers [@Default @Named @Any]. The possible reason for this is that a non-dependent scoped bean is required to provide a public no-args constructor for CDI, so that it can pick up the desired proxy bean at runtime.


1 Answers

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

What does Weld need to do?

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. (Because it extends the beans is why having a protected no-args constructor will suffice. It has to invoke some constructor of the superclass)

Why the limitation?

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 a parameterized constructor to create a proxy is not really an option because you have no context as to what the parameters should be.

The bean might have a constructor with injection (@Inject) but the proxy needs a no-args constructor to be created.

Also it would possibly prevent some scenarios with circular injection. Furthermore it could also trigger undesired initalization of other objects linked to it. You just cannot know what might be happening inside a constructor with parameters.

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.

A life-saver for when you truly cannot have no-arg constructor

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.

like image 176
Siliarus Avatar answered Sep 16 '22 15:09

Siliarus