Site icon Antonio's Blog

Injection with CDI (Part II)

This is the second post based on pure CDI Injection (see Part I) after having talked about how to bootstrap CDI in several environments and how to add CDI to an existing Java EE 6 application. In this post I quickly want to show the different injection points in CDI : field, constructor and setter. To illustrate these different injection points I’ll use a subset of the previous example : a servlet injecting an ISBN generator POJO :

Field injection

Until now, in all the previous posts, you’ve seen the @Inject annotation put on fields (attributes).

[sourcecode language=”java” highlight=”4,8″]
@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

@Inject
@ThirteenDigits
private NumberGenerator numberGenerator;

@Inject
private ItemEJB itemEJB;

}
[/sourcecode]

As you can see in the code above, the @Inject and the qualifiers (here @ThirteenDigits) annotate an attribute. But like many other injection frameworks, in CDI you can also have constructor and setter injection.

Constructor injection

Instead of annotating the attributes, you can add the @Inject annotation on a constructor. If you then need to use qualifiers, you can add them to the attributes of the constructor as follow :

[sourcecode language=”java” highlight=”7,8″]
@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

private NumberGenerator numberGenerator;
private ItemEJB itemEJB;

@Inject
public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator, ItemEJB itemEJB) {
this.numberGenerator = numberGenerator;
this.itemEJB = itemEJB;
}

}
[/sourcecode]

As you can see, the @Inject is not on the field but on the constructor. On the other hand, @ThirteenDigits doesn’t qualify the constructor but the numberGenerator parameter itself (which is logical). You can also mix field and constructor injection if you want (below I use constructor injection and attribute injection on the EJB) :

[sourcecode language=”java” highlight=”6,9″]
@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

private NumberGenerator numberGenerator;

@Inject
private ItemEJB itemEJB;

@Inject
public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator) {
this.numberGenerator = numberGenerator;
}

}
[/sourcecode]

But the rule is : you can only have one constructor injection point. The container is the one doing injection, not you (you can’t invoke a constructor in a managed environment, well, you can, but it will not work as you expect). There is only one bean constructor allowed so the container can do its job of injecting the right references. The following is invalid :

[sourcecode language=”java” highlight=”7,13″]
@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

private NumberGenerator numberGenerator;
private ItemEJB itemEJB;

@Inject
public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator, ItemEJB itemEJB) {
this.numberGenerator = numberGenerator;
this.itemEJB = itemEJB;
}

@Inject
public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator) {
this.numberGenerator = numberGenerator;
}

}
[/sourcecode]

If you have more than one bean constuctor, this is what you get (the error code and message is Weld specific of course) :

[sourcecode gutter=”false”]
WELD-000812 Cannot determine constructor to use for public@WebServlet class ItemServlet. Possible constructors [[constructor] @Inject public ItemServlet(NumberGenerator, ItemEJB), [constructor] @Inject public ItemServlet(NumberGenerator)]
[/sourcecode]

On the other hand, it is syntactically legal to have field and constructor injection at the same time (but useless) :

[sourcecode language=”java” highlight=”4,6,9″]
@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

@Inject @ThirteenDigits
private NumberGenerator numberGenerator;
@Inject
private ItemEJB itemEJB;

@Inject
public ItemServlet(@ThirteenDigits NumberGenerator numberGenerator, ItemEJB itemEJB) {
this.numberGenerator = numberGenerator;
this.itemEJB = itemEJB;
}

}
[/sourcecode]

Setter injection

The other choice is to use setter injection which looks like constructor injection. You annotate the setter with @Inject and you qualify the parameters :

[sourcecode language=”java” highlight=”7,8,12″]
@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

private NumberGenerator numberGenerator;
private ItemEJB itemEJB;

@Inject
public void setNumberGenerator(@ThirteenDigits NumberGenerator numberGenerator) {
this.numberGenerator = numberGenerator;
}

@Inject
public void setItemEJB(ItemEJB itemEJB) {
this.itemEJB = itemEJB;
}

}
[/sourcecode]

When you use constructor or setter injection you need to apply your qualifiers to a parameter. So make sure you have the right @Targets (java.lang.annotation.ElementType.PARAMETER) definition :

[sourcecode language=”java” highlight=”3″]
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface ThirteenDigits {
}
[/sourcecode]

Conclusion


One question you could ask (and that’s the one
I asked to Pete Muir) is : “when do you use field over constructor or setter injection ?”. There is no real technical answer except that it’s your personal taste. In a managed environment, the container is the one doing all the injection work, it just needs the right injection points. However, with constructor or setter injection you can add some logic if needed (not with field injection). But it looks like setter injection was added for backward compatibility with existing Java Beans.

The next article will cover producers, so stay tuned.

Download

Download the code, give it a try, and give me some feedback.

References

Exit mobile version