5
Dependency Injection and CDI

WHAT’S IN THIS CHAPTER?            

  • Introduction to dependency injection
  • Why DI is important in Java EE
  • How to implement DI in plain code
  • How DI is implemented in Java EE
  • Introduction to Content Dependency Injection
  • Key differences between CDI and EJB containers

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

The wrox.com code download for this chapter is found at www.wrox.com/go/projavaeedesignpatterns on the Download Code tab. The code is in the Chapter 5 download and individually named according to the names throughout the chapter.

Dependency injection (DI) is one of the few well-known and accepted design patterns that was not listed in the book by the Gang of Four.1 Today, it has been used widely in modern programming languages both internally and as a best practice to promote loose coupling.

J2EE was designed to handle the most complex systems but failed miserably by overcomplicating the development of even the simpler systems. The original design of J2EE relied on heavyweight complexity and tight coupling, which led to the popularity of frameworks such as Spring and Pico container. In 2004, Martin Fowler published an article on the inversion of control containers and the dependency of the injection pattern.2 Most vendors did not support and encourage developers to use the J2EE container. However, soon the lightweight containers took over, they became officially supported and, even more, Spring became the unofficial de facto standard and led to the redesign of Enterprise Java from scratch.

WHAT IS DEPENDENCY INJECTION?

The dependency injection pattern is based on the idea of inverting the control. Instead of creating hard dependencies and creating new objects either with the new keyword or lookups, you inject the needed resource into the destination object. This approach has many benefits:

  • The client does need not to be aware of the different implementations of the injected resources, making design changes easier.
  • Unit testing using mock objects is much easier to implement.
  • Configuration can be externalized, reducing the impact of changes.
  • A loosely coupled architecture allows pluggable structures.

The basic idea behind DI is to change the place where objects are created and to use an injector to inject the specific implementations to the destination objects at the right moment. This may sound like an implementation of the factory pattern (see Chapter 6, “Factory Pattern”), but the whole concept is much more than simple object creation. Inversion of Control (IoC) changes the whole wiring between objects and lets the injector do the work (most of the time magically). Instead of calling a factory to provide an implementation to the caller, the injector works proactively to determine when a destination object needs the target object and performs the injection in the appropriate way.

IMPLEMENTING DI IN PLAIN CODE

Java did not offer a standard DI implementation out of the Enterprise JavaBeans (EJB) container until the introduction of Context and Dependency Injection (CDI). Although there are various DI frameworks, such as Spring and Guice, it is not difficult to code a basic implementation.

The simplest implementation of DI is a factory that creates the dependency on request via a getInstance() method. Now you’ll implement an example that shows how to do this in plain code.

The simple DI implementation should separate the resolution of dependencies from the behavior of the class. This means a class should have specific functionality without defining how it obtains a reference to the classes it depends on. This decouples object creation from where the object is used: the essence of DI.

You will start by looking at an example in Listings 5-1, 5-2, 5-3 and 5-4 that is highly coupled, and refactor it to use your home-grown DI.

In Listing 5-1, the UserService class provides business logic services for user management, such as persisting the user to the database. In this example, the object creation is done in the constructor. This couples the business logic (the class’s behavior) to the object creation.

You’ll refactor this example by taking the object creation out of your class and putting it in a factory.

In Listing 5-5, an implementation of the UserDataRepository is created and passed to the constructor of the UserService class. You change the constructor of the UserService class to accept this new parameter.

In Listing 5-6, the UserService constructor asks for an instance of the UserDataRepository to be “injected” into the constructor. The UserService class is decoupled from the UserDataReposityImpl class. The factory is now responsible for the creation of the object and “injects” the implementation into the constructor of the UserService. You have successfully separated the business logic from object creation.

IMPLEMENTING DI IN JAVA EE

J2EE did not offer DI out of the box until Java EE 5. Instead, in J2EE, beans and resources were accessed using Java Naming and Directory Interface (JNDI) context lookups. This approach caused hard-wiring and relied on a heavyweight server-based container, which made testing almost harder than writing the actual code.

With the release of Java EE 5 and EJB 3, DI became an integral part of the Enterprise Java platform. To get rid of XML-based configuration, several annotations were introduced to perform injection:

  • @Resource (JSR250) is for injecting data sources, Java Message Service (JMS), URL, mail, and environment variables.
  • @EJB (JSR220) is for injecting EJB.
  • @WebServiceRef is for injecting web services.

With the release of Java EE 6, CDI, and EJB 3.1, DI became a much more capable, and thus more interesting, topic in Java EE.

In EJB 3.1, an interface was no longer mandatory for EJBs. Also, a new EJB web profile was introduced that offers a simplified lighter EJB container. A new and improved injection annotation @Inject was introduced (JSR229 and JSR330), which also provided a common interface for injection between other DI frameworks in the Java realm.

The @Inject annotation DI is type safe because it injects a dependency based on the type of the object reference. If you were to refactor the code in Listing 5-1, you would remove the constructor and add an @Inject annotation to the UserDataRepository field. The code would look something like Listing 5-7.

The CDI container constructs a single UserDataRepositoryImpl instance as a container managed bean and injects it anywhere it finds @Inject annotating a field of type UserDataRepository.

You can inject a container-managed bean into constructors, methods, and fields, regardless of the access modifier, although fields must not be final, and the method must not be abstract.

Some important questions arise. What happens if there is more than one implementation of the UserDataRepository interface? How does the CDI container identify the correct implementation to inject? To disambiguate the concrete implementations of the UserDataRepository interface, you can annotate the concrete class using a developer-defined qualifier.

Imagine having two implementations of the UserDataRepository interface: one for a Mongo DB collection (a document based database) and another for a MySQL database (relational database). You would have to create two qualifiers (one for the Mongo implementation and another for the MySQL implementation), the concrete class would be annotated at the class level with the relevant qualifier, and in the class in which the UserDataRepository is to be injected, a field would be annotated with the same qualifier.

If you refactor the UserService class in Listing 5-7 to use the Mongo implementation of the UserDataRepository, you would add @Mongo to the udr field as follows:

@Inject @Mongo
private UserDataRepository udr;

The use of qualifiers is discussed in more depth below and in Chapter 6.

The @Named Annotation

Another great achievement was the introduction of the @Named annotation instead of String qualifiers. Ambiguities in EJB dependencies were resolved by using a String in the beanName attribute of the @EJB annotation that specified the implementation to be injected: 
@EJB(beanName="UserDataRepository"). The @Name annotation also supports disambiguation with the use of a String attribute. In Listing 5-8, the Mongo implementation of the UserDataRepository is injected into the udr field.

An explicit annotation of the Mongo implementation is required by a corresponding @Named annotation appropriately named. In Listing 5-9, the Mongo implementation of the UserDataRepository interface is annotated with the same String name as that used to disambiguate the injected implementation in Listing 5-8.

The use of Strings to identify dependencies is legacy because it is not type safe and is discouraged in the CDI specification JSR-299. However, there is a use of the @Named annotation that avoids the need to use String identifiers at the point of injection.

  @Inject @Named
  private UserDataRepository UserDataRepositoryMongo;

In Listing 5-9, the name of the implementation to inject is inferred from the name of the field UserDataRepositoryMongo. What is effectively happening is that @Named is being replaced by @Named("UserDataRepositoryMongo").

Context and Dependency Injection (CDI)

Context and Dependency Injection (CDI) brought full-fledged dependency injection and context support to the Java EE platform, which used to be EJB specific and far more limited. After EJB 3 was introduced, JBoss introduced Seam (a web application framework), which had become quite popular, by supporting direct interaction between JavaServer Faces (JSF) and JavaBeans as well as EJB. The success of Seam led to the design of JSR299, WebBeans. Just as Hibernate, a famous Java Persistance Framework, inspired Java Persistence API (JPA) standardization, Seam inspired and formed the core implementation of CDI.

CDI can work with any Plain Old Java Object (POJO) by instantiating and injecting objects into each other. The following types of objects are injectable:

  • POJOs
  • Enterprise resources such as data source and queues
  • Remote EJB references
  • Session beans
  • EntityManager objects
  • Web services references
  • Producer fields and objects returned by producer methods

CDI Versus EJB

Although CDI and EJB seem to be rivals, they work in harmony. CDI can work alone without an EJB container. Actually, CDI can power a desktop application or any web application that doesn’t rely on the EJB container. CDI provides the factory and injection to any JavaBean.

However, EJBs still require the EJB container. Even the simplified architecture of EJBs is more complex than POJOs, so EJBs still need the EJB container. The EJB container provides additional services such as security, transactions, and concurrency that EJBs need.

Simply put, the CDI container is a lighter, powerful, but less functional container for POJOs. Still, both containers are so well integrated that CDI annotations can act as a gateway and standard interface to interact with the EJB container. For example, the @Inject annotation can work with either POJOs or EJBs and can inject any combination of each by invoking the right container to handle the job.

CDI Beans

A container managed bean is little more than a POJO that conforms to some simple rules:

  • It must have a no-argument constructor, or the constructor must declare an @Inject annotation.
  • The class must be a top-level concrete class or must be annotated with @Decorate; it cannot be a nonstatic inner class.
  • It cannot be defined as an EJB.
  • If the bean is defined as a managed bean by another Java EE technology, such as the JSF technology, it will also be managed by the container.

Any class conforming to these requirements will be instantiated and managed by the container and is injectable. No special annotation is required to define the class as a managed bean.

The container looks for bean-inside-bean archives. There are two types of bean archives: explicit and implicit. An explicit archive contains a bean.xml deployment descriptor, which is normally empty. The CDI scans the classes in the archive looking for any class that conforms to the bean requirements detailed earlier and manages and injects any such class that is not annotated with @Vetoed. This annotation excludes that class from being managed by the container.

In some cases, it may not be desirable to allow the container to manage any conformant bean it finds. If you want to restrict what the CDI container considers to be a managed bean, you can define the bean-discovery-mode property in the bean.xml deployment descriptor. Listing 5-10 shows a snippet of the bean.xml file that defines the bean-discovery-mode property as ALL.

The bean-discovery-mode property can take one of three values: ALL, NONE, or ANNOTATED. The ALL property instructs the CDI container to manage all beans that it finds in the archive. This is the default. The NONE property means that the CDI container will manage no beans, and the ANNOTATED property makes the archive behave like an implicit archive. In this case, the container scans for beans with annotated scope types.

An implicit bean archive does not contain a bean.xml deployment descriptor. This signals to the CDI container that the container should only manage beans with a scope. Further details about scoped beans are discussed later in the section, “Contexts and Scope.”

The @Inject Annotation

The @Inject annotation and its capabilities have already been covered. Before CDI in Java EE was introduced, each DI framework offered its own way of injecting resources. When the Java EE CDI container was released to work alongside the EJB container, @Inject annotation became a unique and abstract interface for almost all inject operations. The @Inject annotation lets you use any appropriate container or DI framework referenced for the case.

Contexts and Scope

Context is the difference between EJBs and CDI containers. Each CDI bean’s life cycle is bound to a context scope. The CDI offers four different scopes:

  • @RequestScoped—Duration is a user’s HTTP request.
  • @SessionScoped—Duration is a user’s HTTP session.
  • @ApplicationScoped—State is shared across all users for the duration of the application.
  • @ConversationScoped—Scope duration is controlled by the developer.

A bean annotated with a scope holds state for the duration of the scope and shares that state with any client that runs in the same scope. For example, a bean in the request scope holds state for the lifetime of the HTTP request, and a bean with session scope holds state for the lifetime of the HTTP session. The scoped bean is automatically created when it is needed and destroyed when the context in which it takes part finishes.

The scope annotations are often used to give scope to beans that are used via Expression Language (EL) in Facelet pages.

Naming and EL

A bean annotated with @Named is accessible through EL. By default, the name used in the expression is the name of the class with the first letter in lowercase. To refer to getter methods that start with get or is, omit the get or is part of the method name. Listing 5-11 shows an example.

This is a simple implementation of a named bean that returns a String when the getFullName() method is called. In a Facelets page, you would refer to this method as user.fullname.

<h:form id="user">
   <p><h:outputText value="#{user.fullname}"/></p>
</h:form>

CDI Beans for Backing JSF

As in the previous example, CDI Beans can serve as backing beans for JSF pages. You can access named beans via the name of the bean with a lowercased first letter. You can access Getter/Setter fields and methods within JSF pages using Java conventions. Details of JSF go beyond the scope of this book, but Listing 5-11 demonstrates a basic usage of CDI Beans with JSF.

Qualifiers

This section looks at how you would construct the qualifier classes.

In Listing 5-12, you create a qualifier named Mongo that you can use to annotate fields. If you want to use this annotation on a METHOD, a PARAMETER, or a class/interface (TYPE), you can add it to the @Target annotation.

The discussion regarding the varied use of annotations continues in more depth in Chapter 6.

Alternatives

In the examples so far, you learned how you can disambiguate between two distinct implementations of the UserDataRepository interface by using qualifiers. You normally make this choice of implementation at development time by changing the source code. However, you can also make this choice at deployment time by using the @Alternative annotation and some configuration in the bean.xml deployment descriptor.

Adapting the examples so far, you annotate the two implementations of the UserDataRepository interface with @Alternative and add some configuration XML to the bean.xml file. This is where you decide which implementation to inject.

@Alternative
public class UserDataRepositoryMongo implements UserDataRepository {...}

@Alternative
public class UserDataRepositoryMySQL implements UserDataRepository {...}

The implementation that you use in the application is declared in the bean.xml file:

<beans ...>
    <alternatives>
        <class>com.devchronicale.di.UserDataRepositoryMongo</class>
    </alternatives>
</beans>

Alternatives are often used during the testing phase of development to create mock 
objects.

Stereotypes

You can think of stereotypes as templates that define the characteristics of a specific functionality of a bean type. For example, a bean that is used at the model layer of an Model View Controller (MVC) application requires certain annotations to perform its function. These would include the following:

@Named
@RequestScoped
@Stereotype
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)

Only @Named and @RequestScoped are enough to define a Model bean. Others are required to create an annotation called @Model.

You could apply these annotations on every bean that requires them, or you could define a stereotype called @Model and apply only that to the beans. The latter makes your code much easier to read and maintain.

To create a stereotype, you define a new annotation and apply the required annotations as in Listing 5-13.

Any bean annotated with @Model has a request scope (@RequestScoped) and is visible to EL (@Named). Luckily, the CDI container that comes with this stereotype has already been defined.

A typical use of the stereotype annotation is to combine with alternative annotation so you have a way to annotate mock objects.

Other Patterns via CDI

CDI unleashed a great power to Java EE developers. CDI goes beyond being just a simple DI framework by making the implementation of all those patterns possible with minimal code.

The chapters that follow dive deep into details of these patterns; however, to whet your appetite, here’s a brief introduction to these CDI-powered patterns.

Chapter 7, “Decorator Pattern,” covers the decorator pattern. Decorators wrap a target object to dynamically add new responsibilities at run time. Each decorator can be wrapped by another, which allows for a theoretically unlimited number of decorated target objects at run time. The decorator pattern uses the @Decorator and the @Delegate annotations. You must specify the decoration order in beans.xml.

The factory pattern is covered in Chapter 6. Factories minimize the usage of the new keyword and can encapsulate the initialization process and different concrete implementations. The factory pattern uses the @Produces annotation to mark producer methods. Target object can inject or observe the produced objects.

The observer pattern and events are addressed in Chapter 11, “Observer Pattern.” The observer pattern changes the direction of a message, thus the order of caller and the callee. With the help of the observer pattern, instead of aggressively checking a resource, an object can subscribe to the changes on the resource. The observer pattern in Java EE uses the @Observes annotation and events. The target observer(s) can observe any fired event.

Aspects and interceptors are the focus of Chapter 8, “Aspect-Oriented Programming (Interceptors).” They let you change execution flow at run time. Any aspect or interceptor can be marked to cut the execution and kick in at the given point. This approach enables dynamic changes even on a large code base.

SUMMARY

In this chapter, you have seen the concept of dependency injection in Java EE. The dependency injection concept lets us build loosely coupled systems easier than one can ever imagine. We have seen how dependency injection allows us to eliminate the use of a new keyword, thus, manual object creation.

We also focused on CDI, which unleashes a huge potential by leveraging a whole new container. With the help of CDI, dependency injection can be applied to any object and many patterns that are discussed in this book are easy to implement.

  EXERCISES  

  1. Design a service class which will return some string to the client.
  2. Implement a file reader and inject it to the service you developed before.
  3. This time implement an object which reads the html content as a string from a fixed URL.
  4. Think about what you need to refactor in the service class to be able to inject both data providers with the same reference.
  5. Is there a way to dynamically inject each implementation depending on different circumstances? For example can you make sure the file reader is injected during development but the http reader is used during production?

NOTES

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset