An Introduction to CDI
Nicolas Frankel introduces us to contexts and dependency injection.
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:
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.
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.
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.
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:
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.
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:
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.
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:
With Java EE 6, we can harness the power of AOP without AOP. Like in the previous example, using interceptors is very straightforward. There are 3 steps. Let’s implement a simple timer, for benchmarking purposes. The first step is the declaration of the interceptor. To do so, just use the @InterceptorBinding:
The second step is the interceptor implementation. It uses the @Interceptor annotation, coupled with the previously defined one:
• the method annotated with @AroundInvoke returns an Object
• it uses a parameter of type InvocationContext
The last step is to declare such interceptors in WEB-INF/beans.xml because interceptors are deactivated by default.
The beans.xml also tells the container about how to order the interceptors in case there is more than one. There are two other interceptor types, @PostConstruct and @AroundTimeout (for EJB).
Decorators – guess what – implement the Decorator design pattern. They are very similar to interceptors with two interesting differences:
• a decorator must implement the interface it is decorating (and yet can be abstract, so it does not have to implement the methods)
• a decorator can have a reference to the object it decorates.
It is done through injection Like interceptors, they must be referenced in the beans.xml file in order to be activated. Let’s take a simple example and create an interface which contract is to return an HTML representation of an object:
Now we need a date class that knows its HTML representation. I know the design is quite bad but bear with me, it’s just an example. Should one want a decorator that puts HTML inside <strong> tags, here’s the way:
CDI also implements the Observer design pattern, thus at last enabling simple event-driven development paradigm on the Java EE platform. The basis for it is the event type. An event type is a simple POJO. The Observer is also a POJO: in order for a method of the Observer to be called when an event is fired, just add a parameter of the right event type and annotate it with @Observes:
On the other side, the event producer should have an attribute of type javax.enterprise.Event parameterized with the same event type. In order to fire the event, call event.fireEvent() with an event instance:
Now, when sending a POST request to the servlet, the after- PostEvent() method of the EventObserverService will be called.
Previously, mock service was addressed by calling the setter and passing a newly created instance “by hand”. This is all fine and well in a unit testing case, but we also want to manage integration testing. The situation is thus the following:
• there are two implementations of the same interface on the classpath
• one cannot change the servlet code (for example, add a qualier to the service attribute)
Given the deterministic nature of CDI, we should basically be toast. In fact, nothing could be further from the truth. Just use the @Alternative annotation and CDI will conveniently ignore the annotated class.
What’s the point then to create it in the first place? Remember the unused-till-then beans.xml from above. It will come to our aid, since it accepts <alternative> tags. These tags activate the alternatives.
As such, one could have two beans.xml:
• a basically empty standard context
• and another integration testing context full of alternatives
This article was written with GlassFish v3, which uses Weld v1.0.1, as a platform. Weld is CDI reference implementation, and also a part of the Seam framework.
This article only brushes the surface of CDI. Nevertheless, it’s an attempt at standardization and looks very promising. 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