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 you quoted, in CDI spec, beans will become unproxyable if they don't have no-arg constructor but have ones with args. It's not "just for spec" though in the sense that requirement servers no purpose: the proxy creation mechanisms used by CDI need this. They first create the proxy, then the implementation.
In short, yes.
One alternative that I've used in similar scenario, instead of @ApplicationScoped, is @Singleton pseudoscope. That does work without no-param constructor as it is not using normal scope. This means though that the bean will not be proxied. For my use cases this has been ok. Here's an example class:
(Note though that if you use them for jax-rs resources like I have, jax-rs specification says this:
So it might or might not work, depending on implementation. I used Weld for my class where it works.)
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. (The fact that it extends the beans is also why having aprotected
no-args constructor will suffice. Furthermote it is a reason why 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 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.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.