Bringing Real Dependency Injection to the Platform
How To Use DI in Java EE 6
Java EE 6 is over a year old now. With this new version of the Enterprise Edition came some improvements to the existing specifications (EJB 3.1, JPA 2.0, JSF 2.0...) but also completely new ones (JAX-RS, Bean Validation and CDI). This article is about how to use the new dependency injection model brought by Contexts and Dependency Injection.
Dependency Injection (DI) is a design pattern that decouples dependent components. It is a specific form of inversion of control, "where the concern being inverted is the process of obtaining the needed dependency". The term was first coined by Martin Fowler.
Java is an object-oriented programming language, meaning that the real world is represented using objects. A Book class represents a copy of "H2G2", a Customer represents you and a PurchaseOrder represents you buying this book. These objects depend on each other: a book can be read by a customer and a purchase order refers to several books. This dependence is one value of object-oriented design.
For example, the process of creating a purchase order (OrderService) can be reduced to printing the PO (PrintingService), writing and sending an email to the customer (EmailService) and delivering the books to the customer (DeliveryService). The EmailService can be as simple as "writing a message and sending it to an email address" or more complex such as "writing a message body, a subject, adding bold/italic/underlying to the message, checking spelling, validate the email address and sending the message to the valid email address". The OrderService would end up depending either on a SimpleEmailService or a RichEmailService according to some condition or environment. How would you connect an OrderService to an EmailService ? One solution is to use the good old new operator:
This is simple and does the job. But what if I want to choose between implementations and not just get stuck with Simple-EmailService ? One solution is to pass the implementation to the constructor and leave the external services to choose which implementation they want to use:
So now an external component could use the OrderService with the implementation it needs:
This illustrates what inversion of control is : the control of creating the dependency between OrderService and EmailService is inverted because it's given to an external component, not the component itself.
Since you end up connecting the dependencies yourself this technique is referred to as construction by-hand. In the code above we used the constructor to choose implementation (constructor injection) but another common way is to use setters (setter injection). Instead of constructing the object dependency graph by hand you can use a Factory. This Gang of Four (GoF) design pattern offloads the burden of creating dependencies and assembling objects to a third party (called a factory).
One way or the other, you end-end writing some code. With Java EE being a managed environment you don't need to construct by hand nor use a factory, but instead leave the container to inject a reference for you. One way to think about a DI in a managed environment is to think of JNDI turned inside out. Instead of an object looking up other objects, the container injects those dependent objects for you. This is the so-called Hollywood Principle, “Don't call us?” (lookup objects), “we'll call you” (inject objects).
Dependency Injection in Java EE
Java EE was created in the late 90’s (it was then called J2EE). The first version already had EJBs, Servlets, JMS and so on. From there, each new version added extra specifications (Web Services, JSF...), to reach the number of 28 specifications for Java EE 6. Dependency injection, which was a common concept in other frameworks, arrived in Java EE 5. But it was limited and could be seen as resource injection as you could only inject certain resources (JDBC DataSource, JMS factories or destinations, and JPA Entity Manager) and services (TimerService, User Transaction, Web Services, EJBs) to certain components (mostly EJBs and servlets). It allowed you to simplify component dependencies and let the EJB container deal with the complexities of managing the life-cycle of the resource (instantiating, initializing, sequencing and supplying resource references to clients as required). It is the responsibility of the container to inject a reference to a resource based on the dependency declaration.
This first step taken in Java EE 5 wasn’t enough so two brand new specifications were created in Java EE 6 to bring real dependency injection to the platform and also to bring real consistency between specifications: Dependency Injection (JSR 330) and Contexts & Dependency Injection (JSR 299).
DI and CDI both in Java EE 6
These two specifications are complementary and can't be used one without the other.
Dependency Injection for Java (aka @Inject) defines a set of annotations (@Inject, @Named, @Provider, @Qualifier, @Scope and @Singleton) mainly used for injection. If you download this JSR (which is actually just Javadoc) and read through it, you will be surprised to see that no semantic is defined (i.e. the injection behaviour is not portable across implementation). This specification is implemented in Spring 3, Guice, and also in Java EE 6 with CDI. You will find the DI annotations in the javax.inject package.
Contexts and Dependency Injection gives semantic to JSR 330 and adds more features such as context management, events, decorators and enhanced interceptors (AOP). Further more, CDI allows to extend the platform in a standard way, which was impossible until now. The aim of CDI was to fill all these gaps : give more cohesion to the platform, knit together the web tier and the transactional tier, turn dependency injection into a first class citizen and have the ability to add new extensions easily. DI and AOP are the foundation of many Java frameworks, and CDI could be the foundation of future frameworks or even JSRs. The reference implementation is Weld, an open source project from JBoss. You will find the CDI annotations and classes in the javax.enterprise.inject package.
In a nutshell, dependency injection is the ability to inject components into an application in a typesafe way (yes, typesafe way, which means no XML !), and contexts is the ability to bind the life-cycle and interactions of stateful components to well-defined life-cycle scopes. This article concentrates on the DI part.
Injection already existed in Java EE 5 with the @Resource, @PersistentUnit or @EJB annotations for example. But it was limited to certain resources (datasource, EJB...). With DI you can inject nearly anything (we call that “managed beans”, which are beans managed by CDI) anywhere thanks to the @Inject annotation. Note that in Java EE 6 you can still use the other injection mechanisms (@Resource...) but you should consider using @Inject from now on.
To take the previous example, here is how you would inject a reference of the SimpleEmailService into the OrderService :
As you can see, a simple @Inject annotation on the property (or on the setter or constructor) will inform the container that it has to inject a reference into emailService. If you only have one implementation, CDI will be able to inject it. We then talk about default injection. In fact, the code @Inject SimpleEmailService emailService could have been written @Inject @Default SimpleEmailService emailService. @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.
If you only have one implementation of a bean 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.
I first mentioned that no XML was needed in CDI. Well, it’s not completely true. To make CDI work you need a beans.xml file in your classpath (in the META-INF or WEB-INF directory). The good news is this beans.xml file is usually totally empty (except for alternatives as you'll see later). This file tells CDI to discover beans (aka bean discovery) in the application.
- Dependency injection in Java EE
- Ambiguous injection and qualifiers