Antonio Goncalves is a senior software architect living in Paris. Initially focused on Java development since the late 1990s, his career has taken him to different countries and companies where he works now as a Java EE consultant in software architecture. As a former BEA consultant he has a great expertise in application servers such as Weblogic, JBoss and, of course, GlassFish. He is particularly fond of Open Source and is a member of the OOSGTP (Open Source Get Together Paris). He is also the co-creator of the Paris Java User Group and talks on Les Cast Codeurs podcast. Antonio wrote a first book in French on Java EE 5 back in 2007. Since then he has join the JCP and is an Expert Member of various JSRs (Java EE 6, JPA 2.0 and EJB 3.1). He then published a second book for Apress: Beginning Java EE 6 Platform with GlassFish 3. For the last years Antonio has been talking at international conferences mainly about Java EE, including JavaOne, The Server Side Symposium, Devoxx, Jazoon… He has also written numerous technical papers and articles for IT Web sites (DevX, JaxEnter) or IT magazines (Programmez, Linux Magazine). Antonio is a DZone MVB and is not an employee of DZone and has posted 31 posts at DZone. You can read more from them at their website. View Full User Profile

Injection with CDI (Part I)

05.03.2011
| 5639 views |
  • submit to reddit

download After writing a post about how to bootstrap CDI in your environment and giving you some tips about how to incorporate CDI in an existing Java EE 6 application, I want to talk about injection. Yes, pure injection or how to inject a bean into another one. As you’ll see in this series of two articles, many different cases can happen. Let’s start with a simple one : a straight forward injection.

Default injection

The simplest possible injection case is… simple. You have something, and you inject something into it. Why am I using the word something ? Because until Java EE 5 we could only inject resources (EntityManager, Datasource, JMS destinations and factories…) into certain components (EJBs and Servlets). With CDI, you can inject nearly anything into anything else.

Versions of software used for this arcticle
Java SE 1.6.0_23
GlassFish 3.1
Maven 3.0.2

To illustrate injection I’ll be using the same use case I’ve used in previous articles or in my Java EE 6 book : a CD-Book store.

The class diagram above shows the following components :

  • Book is just an Entity with some attributes and named queries
  • ItemEJB is a stateless EJB (with no interface) doing CRUD operations on the Book thanks to the EntityManager
  • IsbnGenerator is just a POJO that generates a random ISBN number (used for the Book)
  • ItemRestService is annotated with @Path (which designates a REST web service in JAX-RS) and delegates the CRUD operation to the ItemEJB
  • ItemServlet is a Servlet that uses the ItemEJB to display all the books from the database

As you can see, except for the EntityManager that is injected with @PersistenceContext, all the other components use @Inject. Here is a few lines of code of the ItemEJB getting a reference of the EntityManager :

@Stateless
public class ItemEJB {

@PersistenceContext(unitName = "cdiPU")
private EntityManager em;
...
}

The ItemServlet and the ItemRestService are very similar as they both inject a reference of the ItemEJB and the IsbnGenerator :

@Stateless
public class ItemEJB {

@PersistenceContext(unitName = "cdiPU")
private EntityManager em;
...
}

And the IsbnGenertor has absolutely nothing special to it as it doesn’t extends from anything nor it is annotated, it’s just a POJO :

 

public class IsbnGenerator {

public String generateNumber () {
return "13-84356-" + Math.abs(new Random().nextInt());
}
}

In all these cases there is only one implementation to choose from (there is only ItemEJB, only one IsbnGenerator). If you only have one implementation, CDI will be able to inject it. We then talk about default injection. In fact, the code :

@Inject IsbnGenerator numberGenerator

could have been written

@Inject IsbnGenerator numberGenerator

@Default is a built in qualifier that informs CDI to inject the default bean implementation. If you define a bean with no qualifier, the bean automatically has the qualifier @Default. The following code is identical to the previous one.

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

@Inject @Default
private IsbnGenerator numberGenerator;
...
}

@Default
public class IsbnGenerator {

public String generateNumber () {
return "13-84356-" + Math.abs(new Random().nextInt());
}
}

If you only have one implementation of the IsbnGenerator to inject, the default behaviour applies and a straight forward @Inject does the job. But sometimes you have to choose between several implementations, that’s where qualifiers come into play.

In this article I use the term bean, but to be more precise I should say managed bean (ie beans that are managed by CDI). ManagedBeans have been introduced in Java EE 6.

Ambiguous injection & Qualifiers

For a given bean type, there may be multiple beans which implement the type. For example, our application can have two implementations of the NumberGenerator interface : IsbnGenerator generates a 13 digits number and IssnGenerator a 8 digits number. A component that needs to generate a 13 digits number needs some way to distinguish between the two implementations. One approach would be to explicitly specify the class (IsbnGenerator) but that creates a hard dependence between the component and the implementation. Another approach would be to rely on external XML configuration to declare and inject the appropriate bean. CDI uses qualifiers, which are annotations, to get strong typing and loose coupling.

In this article I’m using injection on the attribute, meaning that the @Inject annotation is on the attribute. But with CDI you can also use setter injection or constructor injection.

Let’s say, for some reason, that the ItemServlet creates books with a 13 digits ISBN number, and the ItemRestService creates books with a 8 digits ISSN number. Both (the ItemServlet and the ItemRestService) inject a reference of the same NumberGenerator interface, which implementation will there use ? You don’t know ? CDI doesn’t know either, and this is the kind of error message you will get :

Ambiguous dependencies for type [NumberGenerator] with qualifiers [@Default] at injection point [[field] 
@Inject private ItemRestService.numberGenerator]. Possible dependencies [[Managed Bean [class IsbnGenerator]
with qualifiers [@Any @Default], Managed Bean [class IssnGenerator] with qualifiers [@Any @Default]]].

That means we need to be less ambiguous and tell CDI which bean to inject where. If you come from Spring the first thing that comes to your mind is “let’s use the beans.xml file“. But as this post says, “beans.xml is not there to define beans in XML“. With CDI you use qualifiers (annotations).

There are three built-in qualifiers in CDI :

  • @Default : If a bean does not explicitly declare a qualifier, the bean has a @Default qualifier
  • @Any : allows the application to specify qualifiers dynamically
  • @New : allows the application to obtain a new qualified bean

A qualifier represents some semantic associated with a type that is satisfied by some implementations of the type. For example, you could introduce qualifiers to represent thirteen digits number generator or eight digits number generator. In Java qualifiers are represented by annotations defined as @Target({FIELD, TYPE, METHOD}) and @Retention(RUNTIME). It is declared by specifying the @javax.inject.Qualifier meta-annotation as follow :

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

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

As you can see, I’ve just defined two qualifiers, very easily. Right, so how do I use them now ? Better than words, a class diagram will make it clear.

First of all, the qualifiers need to be applied on the appropriate implementation. As you can see, @ThirteenDigits is applied to IsbnGenerator and @EightDigits to @IssnGenerator :

@EightDigits
public class IssnGenerator implements NumberGenerator {

public String generateNumber() {
return "8-" + Math.abs(new Random().nextInt());
}
}

@ThirteenDigits
public class IsbnGenerator implements NumberGenerator {

public String generateNumber() {
return "13-84356-" + Math.abs(new Random().nextInt());
}
}

Then, the components that inject a reference to the NumberGenerator interface also need to use it as follow :

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

@Inject @ThirteenDigits
private NumberGenerator numberGenerator;
...
}

@Path("/items")
@ManagedBean
public class ItemRestService {

@Inject @EightDigits
private NumberGenerator numberGenerator;
...
}

You don’t need external configuration, that’s why CDI is said to use strong typing. You can rename your implementations to whatever you want, the injection point will not change (that’s loose coupling). Note that a bean may declare multiple qualifiers. As you can see, CDI is an elegant way to have typesafe injection. But if you start creating annotations each time you need to inject something, your application will end up being very verbose. That’s when enumerations can help you.

Qualifiers with enumerations

Each time you need to choose between implementation you create an annotation. So if you need an extra two digits number generator or a ten digits number generator you create more and more annotations. Looks like we are moving from XML Hell to Annotation Hell ! One way to avoid the multiplication of annotations is to use enumeration as follow :

@WebServlet(urlPatterns = "/itemServlet")
public class ItemServlet extends HttpServlet {

@Inject @NumberOfDigits(Digits.THIRTEEN)
private NumberGenerator numberGenerator;
...
}

@Path("/items")
@ManagedBean
public class ItemRestService {

@Inject @NumberOfDigits(Digits.EIGHT)
private NumberGenerator numberGenerator;
...
}

@NumberOfDigits(Digits.THIRTEEN)
public class IsbnGenerator implements NumberGenerator {

public String generateNumber() {
return "13-84356-" + Math.abs(new Random().nextInt());
}
}

@NumberOfDigits(Digits.EIGHT)
public class IssnGenerator implements NumberGenerator {

public String generateNumber() {
return "8-" + Math.abs(new Random().nextInt());
}
}

As you can see I got rid of the @ThirteenDigits and @EightDigits qualifiers and I’m using the single qualifier @NumberOfDigits which as an enumerations as a value (in my example @EightDigits). This is the code you need to write :

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface NumberOfDigits {
Digits value();
}

public enum Digits {
TWO,
EIGHT,
TEN,
THIRTEEN
}
Conclusion

I don’t know about you, but I love it. I really like the way CDI wires beans together without XML, just with pure Java in a type-safe way. Ok, ok, I admit, not everything is beautiful. The first thing I see is the multiplication of annotations in your code. Thanks to enumerations, this can be limited. The other point I can see is the IDE support. Your IDE needs to be cleaver to know that :

@Inject @NumberOfDigits(Digits.EIGHT) NumberGenerator numberGenerator

refers to IssnGenerator. But here I’m talking without really knowing much about the topic. I use Intellij IDEA and the CDI support is just amazing. I can navigate from bean to bean without worrying about knowing the implementation. I suppose NetBeans might have some kind of support… but I wonder if Eclipse does ;o)

The next article will cover alternatives and producers, so stay tuned.

Download

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

References
Published at DZone with permission of Antonio Goncalves, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: