Reputation: 7543
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
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