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

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.


I need to have a protected no-arg constructor just to satisfy the CDI spec? in which conditions does it use the no-args constructor? Why is this a requirement in CDI at all?

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 would serve no purpose: the proxy creation mechanisms used by CDI need this. They first create the proxy, then the implementation.

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?

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:

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

(Note though that if you use them for jax-rs resources like I have, jax-rs specification says this:

Support for constructor injection of JAX-RS resources is OPTIONAL. Portable applications MUST instead use fields or bean properties in conjunction with a @PostConstruct annotated method. Implementations SHOULD warn users about use of non-portable constructor injection.

So it might or might not work, depending on implementation. I used Weld for my class where it works.)