Martijn Burger
Martijn Burger

Reputation: 7543

When is the injected constructor and when is the no-args constructor called in CDI

I was wondering how constructor injection exactly worked in combination with the requirement that a bean class needs a no-args constructor. My conclusion after the tests described below is that the no-args constructor is called twice and then the injected constructor is called. Can anyone explain me why?

To test this behavior I created a HelloProducer class:

public class HelloProducer {

    @Produces
    @Hello
    public String helloWildFly() {
        return "Hello!";
    }

}

and a Hello qualifier:

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Hello {
}

Then I created a bean class that uses this Producer as an injected constructor:

@Stateless
public class HelloBean {

    private final Logger log = LoggerFactory.getLogger("HelloBean");
    private String hello;

    public HelloBean() {
        log.warn("No-args constructor called");
        this.hello = "Hi!";
    }

    @Inject
    public HelloBean(@Hello String hello) {
        log.warn("Injected constructor called");
        this.hello = hello;
    }

    public String getHello() {
        return hello;
    }  

}

So what is it going to be when I call the getHello() method? Hello! or Hi!? Let's test:

@RunWith(Arquillian.class)
public class HelloBeanIT {

    @Deployment
    public static JavaArchive createDeployment() {
        return ShrinkWrap.create(JavaArchive.class, "test.jar")
                .addClass(HelloProducer.class)
                .addClass(Hello.class)
                .addClass(HelloBean.class)
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Inject
    HelloBean helloBean;

    @Test
    public void testSomeMethod() {
        assertThat(helloBean.getHello(), is("Hello!"));
    }

}

Well, that test runs fine, so the final constructor that is called is the injected constructor. But if I look at the logs I see the following:

WARN [HelloBean] No-args constructor called
WARN [HelloBean] No-args constructor called 
WARN [HelloBean] Injected constructor called

So, why is the no-args constructor called twice by CDI before the injected constructor?

Upvotes: 3

Views: 1790

Answers (1)

Siliarus
Siliarus

Reputation: 6753

So, with a bit of digging, I found out what actually happens there.

The no-args constructor invocations happen during proxy initialization while the sole invocation with arguments is the actual object creation (and also what you get to work with in the end).

To elaborate further, your bean is @Stateless - meaning it is both, EJB and CDI bean (EJB by definition and CDI automatically picks it up for you as well). And both these spec operate in a way that they create a proxy object on top of the actual instance and you then just pass around the proxy reference instead of actual instance.

Therefore it goes like this:

WARN [HelloBean] No-args constructor called -> CDI Proxy on top of EJB proxy
WARN [HelloBean] No-args constructor called -> EJB proxy
WARN [HelloBean] Injected constructor called -> actual instance creation

Upvotes: 2

Related Questions