Bringing Real Dependency Injection to the Platform

How To Use DI in Java EE 6 - Part 2

  

Ambiguous Injection and Qualifiers

 

For a given bean type, there may be multiple beans which implement the type. Coming back to our example, a component can choose between two implementations (SimpleEmailService or RichEmailService) of the interface EmailService. How do you make the choice? Most frameworks heavily rely on external XML configuration to declare and inject beans. CDI uses qualifiers, which basically are annotations. That’s how type safe injection comes into play (strong typing).

 

Coming back to the previous code, as you saw, it will work if you only have one implementation, but if you try to run it with more than one implementation of the interface EmailService, this is the kind of error that you will get :

 

 

The injection is ambiguous, CDI doesn’t know which bean to inject (either SimpleEmailService or RichEmailService). It needs some way to distinguish between the two different managed beans. One approach would be for the client to explicitly specify the class that implements the EmailService interface. However, this approach creates a hard dependence (strong coupling) between client and implementation. To have loose coupling and strong typing, CDI uses qualifiers.

 

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 rich or simple email service. In Java code, qualifier types are represented by annotations that you apply to a managed bean defined as @Target({FIELD, TYPE, METHOD}) and @Retention(RUNTIME). It is declared by specifying the @javax.inject.Qualifier meta-annotation as follow:

As you can see, the code above is quite simple: two annotations annotated with Qualifier. These qualifiers are then applied to injection points to distinguish which implementation is required by the client. In Listing 1 the OrderService explicitly defines which implementation by injecting a reference of the @Rich EmailService.

 

If you run this code you will not have any ambiguous dependency any more as CDI knows exactly which implementation of the managed bean to inject. You could even rename the RichEmailService class to RichMailerService and the code will work with no other change as CDI relies on the name of the qualifier, not the name of the implementation (that's loose coupling in action). Note that any bean may declare multiple qualifiers.

Choosing implementation at development time is very useful, but sometimes you need to choose at deployment time. That's when you can use alternatives.

Alternatives

As you've just seen, when you have more than one version of a bean that you use for different purposes, you can choose between them during the development phase by injecting one qualifier or another. Qualifiers let us choose between multiple implementations of an interface at development time. But sometimes you want to inject an implementation depending upon the deployment environment. For example, you may want to use a mock email service in a testing environment. This is possible with alternatives and a bit of XML.

Alternatives are beans whose implementation is specific to a particular deployment scenario. They are annotated with the special qualifier javax.enterprise.inject.Alternative. This is what an alternative to the rich email service could look like:

As you can see the MockEmailService implements the EmailService interface as usual. It is both annotated with @Alternative and @Rich, meaning that this managed bean is an alternative to the RichEmailService. By default, @Alternative beans are disabled and you need to explicitly enable them in the beans.xml descriptor.

 

 

Now if you execute the application with the following code with the beans.xml file, the OrderService will get injected the MockEmailService rather than the RichEmailService.

 

 

If the <alternatives> element is commented out in the beans.xml file, the RichEmailService class is used. You can have several beans.xml file declaring several alternatives depending on your environment (development, production, test...).

 

If you think of qualifiers being an aggregation of annotations, then you can refactor your code and create a @Mock qualifier which is an @Alternative and a @Rich email service. You will then get the following code :

Now you can go back to the MockEmailService and change it to being a @Mock. The behaviour is exactly the same as having @Alternative and a @Rich together in the same managed bean:

@Inject, alternatives and qualifiers allow you to have a flexible and type-safe injection model. But the beauty of it is that you can use this exact injection model in most of Java EE 6 components.

 

Inject Everything Everywhere


Java EE 5 was about injecting resources (Datasource, JMS factories...) to certain components (mostly EJBs). Java EE 6 is about injecting nearly everything (or so called managed beans) to nearly all the components. So far you've seen plain old Java objects (POJOs) injecting other POJOs. But you can inject an EJB into a Servlet or a RESTful Web Service. Below you can see a servlet injecting our rich email service as well as an EJB that deals with orders. As you can see, the @Inject annotation is used exactly the same way in both cases :

Bootstraping CDI in Several Environments


Both JSRs 299 and 330 are included in Java EE 6 as well as the Web Profile 1.0 (which is a subset of the entire platform). This means you don't have to do any extra work to use injection in a Servlet 3.0 or EJB 3.1 container. It works out of the box. But the beauty of CDI is that it doesn't require Java EE 6. You can use CDI with simple POJOs in a Java SE environment, as well as some Servlet 2.5 containers (Tomcat 6.5 or Jetty). Of course, because it's not built in, you'll need a bit of configuration. And because this feature is not standard, each different environment will need its own configuration. But it then works fine and you can use all the previous examples within Tomcat 6.5 or even just with Java SE 6.

But There is More to CDI

Injection was already popular in other frameworks (PicoContainer, Spring, Guice) and it finally became a first class citizen in Java EE 6. But there is more to CDI that just dependency injection.

One of the key features of CDI is it knits together the web tier and the transactional tier (i.e EJBs). So the idea is to make a managed bean (POJO, EJB...) accessible through expression language (EL) and therefore be able to use it in JSF and JSP pages. For that, you just need to give your bean a name with the @javax.inject.Named built-in qualifier:

The @Named qualifier allows you to access the managed bean through its name (which by default is the class name with first letter in lower-case but you can override it with @Named("MyEmailService")). Then you can invoke an attribute (via its getter and setter) or a method straight in your JSF or JSP page. The following example shows a JSF page with an input text to enter the email body and a button to send the email :

 

Another interesting feature in CDI are scopes and contexts. The scope of a bean determines its life-cycle. Until now Java EE had stateless and stateful components which were not tight together (you couldn’t easily tight the life duration of a HTTP session to a stateful EJB for example). You had to twist built-in web scopes (request, session and application) to fit the back-end transactional layer. With CDI things are much easier and extensible.

 

CDI defines four built-in scopes and gives you extension points so you can create your own :

 

• @RequestScoped

• @SessionScoped

• @ApplicationScoped

• @ConversationScoped

 

The three first scopes are well known. For example, if you have a session-scoped bean, LoggedInUser, the RichEmailService that is called in the context of the same HttpSession will see the same instance of LoggedInUser. This instance will be automatically created the first time a LoggedInUser is needed in that session, and automatically destroyed when the session ends.

 

 

The conversation scope, however, is a new one that acts a bit like the session scope: it holds state associated with a user, spans multiple requests to the server but is demarcated explicitly by the application.

 

Conclusion

 

Dependency Injection (JSR 330) and Contexts & Dependency Injection (JSR 299) both bring a standard, portable and type safe support for dependency injection to Java EE 6. CDI adds extra features such as scopes and contexts but also enhanced interceptors, decorators and events. But the noble goal of CDI is to be a foundation for frameworks which will extend the platform and integrate with other technologies. Therefore, CDI exposes a set of SPIs for developers in order to create portable extensions. The Java EE platform becomes finally extensible in a standard way.

The good news is that you don’t need to use a Java EE 6 application server such as GlassFish 3.x or JBoss 6.x. You can bootstrap CDI in several environments and get all its benefits with your Servlet 2.5 container (Jetty or Tomcat 6.x) as well as your Java SE application. Being a standard, you can choose from several implementations (OpenWebBeans from Apache, Weld from JBoss or CanDI from Caucho) and get started with it now. Enjoy.

Pages

Antonio  Goncalves
Antonio Goncalves

What do you think?

JAX Magazine - 2014 - 03 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions