Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 228 posts at DZone. You can read more from them at their website. View Full User Profile

CDI - An Overview: Part 1

05.25.2010
| 10347 views |
  • submit to reddit

I may be a Spring fanboy but I’m also convinced a technology should embrace standards. Although Spring is now a de facto standard, it is in competition with other products, like Google Guice for example. It makes my work as an architect harder since my craft is to devise the most perennial solutions: standards are my allies in achieving this goal.

CDI, formerly known as JSR 299, is an attempt at describing a true standard on Dependency Injection. What makes CDI appealing at first glance is that both SpringSource and Google took a seat in the specifications team. CDI is a part of the Java EE 6 stack, meaning an application running in a Java EE 6 compatible container can leverage CDI out-of-the-box.

Thus, for a Maven application, all that is required is to add the following dependency:

<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.0-SP1</version>
<scope>provided</scope>
</dependency>

The basics

How is it done? Let’s take a simple example. I still want to use a lighweight service layer: I just need a reference to such a service in my servlet. In order to do that, just add a reference to the service as an attribute and annotate it with @Inject:

public class WeldServlet extends HttpServlet {

@Inject private HelloService helloService;

...
}
Note for Guice users: notice this is the same annotation, only in a standard package (javax.enterprise).

That’s all! No fancy XML configuration, no JNDI resource to use, no nothing. The only thing to do is to follow what I call the Highlander rule:

There can be only one.

This rule enforces that there must be one and only one class on the classpath that extends HelloService, thus fulfilling the interface contract. This is common sense: the injection must be deterministic and you can’t if you have more than one implementation to choose from.

Activation

In truth, if you just use the @Inject annotation, you probably will be faced by a NullPointerException. For CDI to activate, you’ll need to have a XML configuration file named beans.xml under WEB-INF for web applications or META-INF for jars. This file can be empty but is mandatory in order for CDI to bootstrap.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

Qualifiers

Yet, such condition is not met most of the time since you’ll probably have at least another mock implementation for your unit tests. A concept is missing somewhere: it is the qualifier. Qualifiers add a quality to injection so that the Highlander rule is enforced between all classes that meet all the injection qualifiers. Thus, one can add a distinguishing qualifier to the mock service to resolve our quandary. Let’s design such a qualifier:

  • to be considered a qualifier, it uses the @Qualifier annotation
  • since injection is done at runtime, the retention policy must be runtime
  • target will be type, since it’s the class itself that will be annotated

The result is the following:

@Qualifier
@Retention(RUNTIME)
@Target( { TYPE })
public @interface Mock {}

Just annotate your mock service with @Mock and presto, the injection will succeed again. We haven’t seen how to inject the mocked service, but please bear with me, it will be adressed later.

Setter injection

In fact, this situation should not really happen (at least if you use Maven), since your standard classpath and your testing classpath should be different. Moreover, your unit testing should IMHO not use injection. This would slightly change my servlet to be used both in standard context and unit testing context: I will need to be able to inject in the former case and to set manually in the latter. This is not a problem, since CDI also accepts setter injection like in the following snippet:

public class WeldServlet extends HttpServlet {

private HelloService helloService;

@Inject
public void setHelloService(HelloService helloService) {

this.helloService = helloService;
}
...
}

More qualifiers

As we saw, the qualifier use-case from above was not a good example. A much better one would be the need for a servlet to report an error. There are much ways of reporting this: mail, log, SMS, etc. The service used to report would be dependent of the servlet, meaning all the services should be available on the classpath. Now, as we have seen previously with @Mock, each service would be annotated with @Mail, @Log, @SMS, etc. What we did not see is how to inject the right service. Nothing could be easier, just tell CDI that which service you need by providing the needed qualifier:

public class WeldServlet extends HttpServlet {

@Inject @Mail private ReportService reportService;

...
}
When you do not define any qualifier, CDI will use one for you under the cover, @Default. That’s why just annotating the mock service with @Mock succeeded: the real service was annotated implicitly with @Default and that was enough to fulfill the Higlander rule.

Qualifiers with attributes

Using the previous method will likely lead to an exponential proliferation of qualifiers that contradict Java EE 6 goals of readibility and maintainability. CDI will still let you reach them goals with annotation members. Now, the classes are:

public enum ReportType {

MAIL, SMS, LOG; // Adding one more type here is easy
}

@Qualifier
@Retention(RUNTIME)
@Target( { TYPE, FIELD })
public @interface Report {
ReportType value();
}

@Report(MAIL)
public class MailReportServiceImpl implements ReportService {...}

public class WeldServlet extends HttpServlet {

@Inject @Report(MAIL)
private ReportService reportService;
...
}

Creating another reporting service consists of creating the service implementation itself and adding a value to the enumeration.

Singletons

Traditionally, services are stateless and, as such, have little interest in being instantiated more than once: it’s a good practice to make them singletons. Frameworks such as Spring make container-managed beans singletons as a default. Singletons creation is a feature of CDI but beware that singletons should be explicitly marked as such:

@Singleton
public class HelloServiceImpl implements HelloService {
...
}

Scoped beans

Singletons are only a particular case of a scope. In Java EE, scopes are well-defined regarding web applications: application, session, request and page. CDI does not manage page scope and adds a conversation scope that is tied to JSF. Scope usage in CDI is similar as Spring: injected bean will be tied to the defined scope and its visibility restricted to that scope.

Information relative to settings and preferences could well take place in a session-scoped bean for instance. Just add the right scope annotation to this bean:

@SessionScoped
public class UserSettings implements Serializable {

private static final long serialVersionUID = 1L;
...
}
At this point, you should probably able to adress 80% of your needs. The next article will tackle advanced subjects like producers and alternatives.

To go further:

  • Commons annotations (JSR-250) page: Commons annotations has annotation for DI in Java (@Resource)
  • CDI (JSR-299) page: amazingly enough, CDI is about DI in Java EE
  • Weld’s documentation: Weld is CDI JBoss implementation and also the reference implementation
  • Article on the merits of JSR-299 compared to the merits of JSR-330

 

From http://blog.frankel.ch/cdi-an-overview-part-1

Published at DZone with permission of Nicolas Frankel, 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.)

Comments

Bozhidar Bozhanov replied on Tue, 2010/05/25 - 9:27am

I'm not sure whether google and spring people took part in CDI. They took part in another JSR, specifying javax.inject. JSR-299 was led by Gavin King (JBoss), and if you check the expert group there is noone from google or springsource.

Nicolas Frankel replied on Wed, 2010/05/26 - 4:53am in response to: Bozhidar Bozhanov

Good comment. And yet, amazingly enough, JSR 330 annotations led by Google and Spring are the same: Qualifier, Scope, Singleton, Named and Provider. That's not a coincidence and Adam Bien clarified it here.

In short, JSR-299 encompass JSR-330. Not to mention that JSR-250 adress DI in Java SE. And they say it's simple :-)

Wujek Srujek replied on Wed, 2010/05/26 - 4:08am

It's 330, not 300.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.