There Can Only Be One

An Introduction to CDI

I’m convinced a technology should embrace standards when possible. Regarding Dependency Injection, the market regards Spring as a de facto standard. However, it is competing with other products, like Google Guice for example. It makes my work as an architect harder since my craft is to devise the most durable 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, at least on paper. Thus, for a Maven application, all that is required is to add the following dependency:

The Basics

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

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 can be called 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 it cannot if CDI has more than one implementation to choose from.

Activation


In truth, just using the @Inject annotation, one probably will be faced by a NullPointerException. For CDI to activate, one needs 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.

 

Qualifiers


Yet, such conditions are not met most of the time since one will probably have at least another mock implementation for integration 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:

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 when using Maven), since standard classpath and testing classpath should be different. Moreover, unit testing should not use injection.

This would slightly change the servlet to be used both in standard context and unit testing context: one will need to be able to inject in the former case and to set it manually in the latter. This is not a problem, since CDI also accepts setter injection like in the following snippet:

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 many 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, one just has to tell CDI which service one needs by providing the needed qualifier:

When not defining any qualifier, CDI will use one 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's goals of readibility and maintainability. CDI still lets us reach those goals with annotation members. Now, the classes are:

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:

 

 

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 is 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:

At this point, one should probably be able to address 80% of one’s needs. Nonetheless, CDI goes further.

 

Producers


Previous examples cannot resolve all our use-cases. Some of these include:

• injection of random values

• injection of context-dependent value

• in general, places where the injection process cannot be narrowed down to a simple new()

 

These hint at a very well-known pattern, the factory. Factories are implemented in JSR-299 as producers. Let’s take a simple example, the injection of a connection from a data source. The code that gets the connection either creates it with a direct connection to the database or retrieves it from a data source pool. In the latest case, the following code would fit:

 

     

Pages

Nicolas Frankel
Nicolas Frankel

What do you think?

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

Comments

Latest opinions