JavaEnterprise JavaA Simple Guide To CDI for Java EE

A Simple Guide To CDI for Java EE

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Context Dependency Injection (CDI) is a mechanism to provide service (dependency) to a dependent object (client). Once a particular service is ascertained in a system such as a database transaction, it can be built into a service component. This component then can be injected to any client that wants to avail its services. Clients thus do not need to rebuild it from scratch each time such a service is required. CDI not only promotes re-usability by dependency injection but also glues together various layers of an enterprises application in a Java EE framework. In this article, I have used the terms services, beans, and component almost interchangeably although there are subtle differences among them. Let’s keep this technical jargon aside for a while and try to understand CDI in particular and DI in general in as simple manner a as possible.

The Idea of CDI

Actually, Inversion of Control (IoC) is the original term for this software design pattern. Martin Fowler rechristened it with an appropriate name called Dependency Injection (DI).

“…I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.” – Martin Fowler

The connotation ‘dependency injection’ actually refers to the mechanism of injecting loosely coupled but type-safe components into an application. From the Spring framework to Java EE (apart from these two, there are other frameworks as well that implement DI, but Spring and Java EE are perhaps most popular) the implementation patterns of this concept have some distinctive differences; however, the core idea remains the same. In thhe Java EE framework, these components are not explicitly created by the client; rather, the responsibility is delegated to the Java EE container to instantiate the injected component implicitly. ‘context’, on the other hand, refers to the scope of the CDI components. At the core, they are nothing but a POJO of stateful beans. Stateful beans are those that retain information across invocation. Because components are instantiated by the container, the life cycles of these components are also maintained by the container itself.

The Role of CDI in Java EE

Any application, especially enterprise, is created in a layered architecture such as a presentation layer, business layer, and persistence layer. Each layer needs to interact with the other uniformly, or as much as possible in a loosely coupled but type-safe fashion. CDI, after its inception in the Java EE framework, bridged the gap of reusable service components between layers. For example, earlier, the presentation layer was focused on the usability of the system and had access to the transactional resources. With the inception of CDI in the Java EE framework, services now can be injected into the client object without modifying its content. This mechanism, for example, unified Enterprise JavaBeans (EJB) with JavaServer Faces (JSF) with the help of service objects called Managed Beans.

Demystifying1
Figure 1: Java EE application architecture without CDI

Demystifying2
Figure 2: Java EE application architecture with CDI support

Delving into Code Intricacies

In the following example, Book is a simple interface, implemented by the BookEJB class. The context part of the CDI here is ascertained by the annotation @ApplicationScoped. There are many other ways the context can be scoped, as we shall see down the line. BookClient injects the service component with the help of the @Inject annotation. Observe that the classical way of instantiation of book is quite different from CDI book2. Instantiation of the book2 object is accomplished implicitly by the container; therefore, ‘new’ is not required and also they do not require a compile time dependency on the actual implementation of classes. In classical instantiation, a concrete class is assigned to the interface reference whereas in CDI we only provide the interface references and the container intelligently instantiates the appropriate concrete classes implicitly. As a result, the coupling between BookClient and Book object can be modified either statically or dynamically.

public interface Book{
   public String getGenre(String title);
}

@ApplicationScoped
public class BookEJB implements Book{
   @Override
   public String getGenre(String title) {
      return "Science Fiction";
   }
}

public class BookClient {
   // classical instantiation
   private Book book=new BookEJB();
   @Inject
   // instantiating via DI
   private Book book2;
}

@Inject defines the injection point. There are three points where this injection can be defined in a bean, such as constructor declaration, method declaration, or field declaration. In the preceding example, the injection point is defined in a field declaration. To define it in a constructor declaration, we can modify BookClient as follows:

public class BookClient {
   private Book book2;
   @Inject
   public BookClient(Book b){
      this.book2=b;
   }
}

And,to specify the injection point in a method declaration, we can modify BookClient as follows:

public class BookClient {
   private Book book2;
   public BookClient(){
   }
   @Inject
   public void setBook(Book b){
      this.book2=b;
   }
}

Scopes of CDI

Context Dependency Injection in Java EE has the following five types of scope:

  • Application Scope: A @ApplicationScope annotation ascertains that the instance be created only once during the lifetime of the application. The class is triggered to instantiate for the first time when the actual injection occurs and is available to share across the application. The instance gets destroyed only when the application is closed.
  • Dependent Pseudo Scope: A scope associated with the @Dependent annotation is bound to a single object and it is the default scope applied by the container when no scope is defined explicitly. Because service components with such scope are associated with a single object, they are not shareable between client applications.
  • Request Scope: A @RequestScope annotation refers to a bean created once per request and destroyed when the request scope ends. During the lifetime of the single request, this components remains active to be shared by all beans involved in a single request.
  • Session Scope: To share the service component across multiple request within the same HTTP session, @SessionScoped annotation can be used. Once the HTTP session begins, a service component is instantiated and maintained by the container until the session is expired or explicitly closed.
  • Conversation Scope: A @ConversationScope annotated service component is live till the life cycle of a conversation. Conversation can be either transient or a long-running state. A conversation can a long-running procedure; for example, in a web application a registration process may need to fill in a form across multiple pages. Thus, conversation can begin by Conversation.begin() and mark its end by Conversation.end().

Dependency Resolution

Imagine a situation where there are more than one BookEJB class that implements the interface Book. How would the container know which class to instantiate at the injection point declared with @Inject annotation? This is clearly an ambiguous situation.

public interface Book{
   public String getGenre(String title);
}

public class BookEJB implements Book{
   @Override
   public String getGenre(String title) {
      // ...
   }
}

public class BookEJB2 implements Book{
   @Override
   public String getGenre(String title) {
      // ...
   }
}

public class BookClient {
   @Inject
   // ambiguous injection point.
   // Choose BookEJB or BookEJB2 ???
   private Book book2;
}

The CDI container will complain with an error message of ambiguity if the preceding code is run. However, Java EE provides three ways to resolve such a situation:

  • Using Qualifiers
  • Using Alternatives (not discussed)
  • Using Producers (not discussed)

Qualifiers

Qualifiers are more common, so let’s get a hint of them. Qualifiers enable the client to inject service components without the knowledge of choosing any particular concrete class implementation. Thus, it decouples the CDI bean with one of the primary characteristics of OOP, called polymorphism and dynamic binding. So, the example above can be modified as follows:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ScienceFiction {
}

@ApplicationScoped
@ScienceFiction
public class BookEJB implements Book{
   @Override
   public String getGenre(String title) {
      return "Science Fiction";
   }
}

public class BookClient {
   @Inject
   @ScienceFiction
   private Book book2;
}

This is one of the ways to resolve ambiguity. CDI in Java EE provides qualifiers such as @Default, @Any, and @Named to modify the unambiguous nature achieved through qualifiers.

@Default

When no explicit qualifiers are defined, the CDI container assumes it as @Default. This is suitable for a situation when there is a single implementation service cmponent and the CDI container can easily inject without ambiguity. Thus, the following code can be written without any behavioral change in the application.

@Default
public class BookEJB implements Book{
   @Override
   public String getGenre(String title) {
      return "Science Fiction";
   }
}

@Any

This qualifier is suitable for iterating over all the implementation of the service components class.

public class BooksClient {
   @Inject
   @Any
   private Instance<Book> books;
   public void showBooks(){
      for(Book book: books){
         System.out.println(book.getTitle());
      }
   }
}

@Named

This qualifier is used to make the service components accessible to the web tier through Unified Expression Language.

public class BookClient {
   @Inject
   @Named("mybook")
   private Book book2;
}

Conclusion

The article mainly scratches the surface of the CDI aspects of Java EE. There are more to it when delved deeper. CDI is an excellent feature introduced in Java EE. To tell you the truth, I was more more interested in the Spring framework for incorporating dependency injection in an enterprise application. But, CDI brought back the inclination towards the Java EE framework. There are some distinctive differences when you work with DI in Spring vis-a-vis the Java EE framework. I tried to make CDI as simple as possible in this article with less technical jargon. The best way to appreciate CDI in Java EE is to try it in your code.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories