The Context and Dependency Injection service

CDI stands for Context and Dependency Injection. It was originally standardized as JSR-299. The Hibernate object relation mapper inventor and Ceylon lead developer, Gavin King submitted the proposal called Web Beans to the Enterprise-expert group in 2006. The name of the JSR was changed in 2009 from Web Beans to CDI for the Java EE platform. The JSR-299 specification for CDI was aligned with the specification JSR-330, dependency injection for Java, which was jointly developed by Guice creator "Crazy" Bob Lee and Spring Framework creator, Rod Johnson.

CDI is upgraded to version 1.1 for Java EE 7 standard and the JSR is 346.

CDI was inspired and influenced by other existing dependency injection frameworks including SEAM, Guice, and Spring Framework. CDI features stronger typing than SEAM, and relies on lesser external XML configuration than Spring Framework.

The original purpose of CDI was to unify the managed bean component model in Java Server Faces with the then EJB component model. However, CDI now far exceeds the original remit in Java EE 7 as the universal component model for the Enterprise.

The first responsibility of CDI is the context. CDI provides the lifecycle management of the stateful components to well-defined, but extensible lifecycle context.

The second responsibility of CDI is dependency injection. CDI provides the ability to inject dependencies (components) into an application in a typesafe way, which includes the configurability to decide which component implementation is injected at the deployment stage.

  • CDI is a framework available in both the client and server.
  • CDI decouples the client from the server and thus supports loose coupling. This means the target implementation can vary without affecting the client.
  • CDI is all about automatic lifecycle management with collaborating components. CDI provides the components with contextual information. The strong typing of the CDI model means that the errors are caught at compilation rather than encountering a ClassCastException at the execution time.
  • The stateful components can have their dependency injected safely and can interact with other services by simple calling methods.
  • CDI also has a lifecycle event model. Interested objects can register themselves to listen to the event notifications.
  • CDI has the ability to decorate the injected components and the ability to associate the interceptors with the components in a typesafe fashion.

The following diagram illustrates the built-in contextual scopes inside the CDI container:

The Context and Dependency Injection service

Beans and bean types

A bean in CDI is a source of the contextual objects that define the application state with or without a logic. Almost any Java object can be treated as a CDI bean.

Beans are instantiated by the CDI container and their lifecycle is determined by the stateful context that they belong to. In other words, all CDI beans havea stateful context.

CDI 1.1 chiefly describes the environment around the Java EE environment, although the implementation such as JBoss Weld can execute in the Java SE standalone application.

Inside a Java EE environment there are components known as managed beans. A CDI bean is one that is managed by the CDI container, whereas the EJB container manages EJB, and the Servlet container manages a Java Servlet. In Java EE 7, the CDI container is different from the other two containers, EJB and Servlet, in terms of managing the stateful component beans by the contextual instances.

Formally, a CDI bean has the following attributes:

  • One or more bean type that is not empty
  • Associated with at least one qualifier
  • Has a determined and well-defined CDI scope
  • Has a set of interceptor bindings
  • Has a bean implementation
  • Optionally can have an expression language (EL) bean name

In the CDI specification, the bean type refers to the managed object inside the container. The visibility of the bean type is defined from its scope and also lifecycle. It is worth remarking that almost all the Java types can be CDI bean types.

The Java types that can be CDI bean types are as follows:

  • A bean type may be a Java interface, a concrete class, or an abstract class, and it may be declared final or have final methods.
  • A bean type may be a generic type with the type parameters and variables.
  • A bean type may be an array type.
  • A bean type may be a primitive Java type, which means that the corresponding wrapper types will be used and instantiated. The wrapper types are defined in the package java.lang.
  • A bean type may be raw type, which is a Java Collection type that is parameterized as compilation type (Pre Java SE 5).

Given the preceding rules most non-Java EE plain old objects can be automatically treated as CDI bean types and no special declarations are required. The CDI container behind the scenes will proxy the bean instances and it uses the Java Reflection API with the byte-code manipulation. Therefore, there are certain circumstances where the container cannot create a certain bean type.

The exceptions to the rules are as follows:

  • The bean type does not have a public default no-argument constructor
  • The bean type is an inner class that is declared not static
  • The bean type is not a concrete class
  • The bean type is annotated with @Decorator
  • The bean type is a class that declares a final or has a final method
  • The bean type is annotated with the EJB component defining annotation or declared as an EJB class in the deployment XML configuration ejb-jar.xml
  • The bean type is an array or a primitive type

Let us summarize some of these definitions into a handy reference as follows:

Term

Definition

Bean type

The bean type is the type hierarchy that the bean provides, which of course, is the interface or class, and ancestor types. The CDI injection container always uses the type as the primary identifier for determining whether a bean will provide an instance.

Qualifier

A qualifier is a way to distinguish between multiple beans that implement a desired bean type. Qualifiers are the type safe annotations and allow a client to choose between multiple bean-type implementations.

Scope

CDI beans all have a well-defined scope, which determines the lifecycle and the visibility of the instances. The CDI scopes are fully extensible and the standard provides built-in scopes, which includes the request, session, application, and conversation scope. The beans can also be dependent and inherit the scope of their injection scope.

EL name

A bean may define an EL name. The facility is provided for non-type safe access. EL names tend to be used by the Java Server Faces views. They can only be used by external and extension frameworks, which are built on top of Java EE standard.

Interceptors

A CDI interceptor is a feature that allows the developers to implement crosscutting concerns, such as security or logging as a bean's methods are invoked. Interceptors are a step up from the classic decorator design pattern.

Implementation

All CDI beans by definition provide an implementation of the types that define them. Implementations are typically in a class.

Basic injection

The key principle of dependency injection, from the definition, is that only the beans instantiated by the CDI container are managed. The CDI container is the external lifecycle provider and the way you behave with it is to respect the cardinal rule, "Don't call us, we'll call you.", The Hollywood Agency principle.

Note

Before CDI, in Java EE 5 specification there was already injection from the EJB container objects @EJB, @PersistenceContext, @PersistenceUnit, and @Resource. Unfortunately only components known to the application server could be injected in such a scheme. CDI is a general-purpose dependency injection framework and it is type safe.

Let us assume we are able to run inside a CDI container, and we are building an airline reservation application. We will define a couple of types defined by their contract, such as an airline and a payment service.

Some Java interface declarations for a software domain-flight reservation system are as follows:

interface CreditService {
  void check();
  }

interface Airline {
  void reserve();
  }

The context for these bean types CreditService and Airline implies a user story about making an airline reservation. For now, we will assume that the lifetime of the bean typed live for the duration of the application.

Field injection

For our first example, let us create an airline reservation system as a simple CDI bean.

public class ReservationService {
  @Inject Airline airline;
  }

This class ReservationService uses a CDI managed bean and it does not yet do anything special. We do, however, declare a dependency on an Airline instance though. The annotation @javax.inject.Inject declares that the field airline will be associated with the default Airline type object. We call this field injection. Let us proceed a bit further.

Setter injection

public class ReservationService {
  @Inject
  public void setAirline(Airline airline) {
    /* Do something here */
  }
}

We can attach the @Inject annotation to a setter method too.

What happens if we are in a situation where another colleague has developed the code already and there was a method to initialize the object already there. CDI can also help us in this situation. Look at the following declaration for a ReservationService class:

public class ReservationService {
  @Inject
  public void startVTTYConnection(Airline airline, PaymentService ps) {
    /* ... */
  }
}

CDI copes with the initialization methods seamlessly. In fact, CDI allows a web bean to have multiple injection and initialization points.

Constructor injection

CDI can also inject the dependencies into the constructors. It is all quite type safe as follows:

public class ReservationService {
  @Inject
  public ReservationService(Airline airline, PaymentService ps) {
    /* ... */
  }
}

Wherever a constructor is declared with the @Inject annotation, the CDI container injects any dependent instances that it finds in the contextual scope of the target bean type. The CDI container also injects any necessary field instances in the bean type by the time the constructor is called that are appropriate to the field's contextual instance. We will talk more about scope later in this chapter.

Note

For constructor injection, a CDI bean type can only have one constructor with the injection points.

If the bean does not declare a constructor with a @Inject annotation, the CDI container will call the no-arguments constructor. The default is called only if there are no other constructors defined.

The CDI injection is type safe and exceedingly appropriate for simple POJOs.

Qualifiers

Qualifiers allow CDI to differentiate by beans with the same type. A qualifier configures a specific bean type to be injected into the target object.

Qualifiers are defined with the Java interface annotations. They are defined as @Target({METHOD, FIELD, PARAMETER, and TYPE}) and @Retention(RUNTIME). There are standard qualifiers in CDI, but you may also define your custom qualifier for your own projects.

Let us define two more example qualifiers.

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

This is for a credit service provider to annotate the short-term applications.

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

And this one is for the long-term applications.

Using the knowledge we now have, we can simply write a CDI implementation for a short-term provider.

interface CreditProvider {
  double computeAPR(double criteria, double base);
}

@ShortTerm
class HighStreetCreditProvider implements CreditProvider {
  double computeAPR(double criteria, double base) {
    return 24.753;
  }
}

The class HighStreetCreditProvider can be injected into a target, if the injection point specifies the @ShortTermCredit annotation.

class CreditService {
  @Inject @ShortTerm CreditProvider shortTermProvider;
  @Inject @LongTerm  CreditProvider longTermProvider;
  
  public void processCredit() {/* ... */}
  /* ... */
}

Qualifiers also work with the CDI productions. The following is a long-term credit provider that provides more reasonable annual percentage age albeit for a much longer term:

class GuiltsCreditProvider implements CreditProvider {
  double computeAPR( double criteria, double base ) {
    return 1.41457;
  }
  
  int months() {return 66;}

  @Produces
  @LongTerm
  public CreditProvider createCreditProvider() {
   return new GuiltsCreditProvider();
  }
}

Built-in qualifiers

Here is a table of the built-in qualifiers in Context and Dependency Injection.

Qualifier Name

Description

@javax.enterprise.inject.Any

This is the qualifier given to every bean instantiated and managed by the CDI container. It is also the qualifier supplied to the injection points. The only exception is where declaration is with the @New qualifier.

@javax.enterprise.inject.Default

If a bean does not explicitly declare a qualifier other than @Named, the bean has the qualifier @Default.

@javax.enterprise.inject.Named

This qualifier gives a CDI bean a name, which allows the JSF view or other presentation toolkit to refer it. Note that this access is not strongly typed at the compilation time.

@javax.enterprise.inject.New

The qualifier @New annotation causes a new instance to be created by the CDI container instead of using the contextual instance. In CDI 1.1, the use of @New is deprecated.

The CDI classpath scanning

How does CDI know exactly what classes to find in a JAR module? CDI scans the modules in an Enterprise application. In Java EE 6, the CDI container scans the Java archive files and searches for a beans.xml XML-deployment descriptor. For a simple JAR or an EJB module, it expects to find META-INF/beans.xml. In a WAR file, this file should be in the location WEB-INF/beans.xml. The beans.xml file is not meant for declaring the CDI beans, unlike Spring Framework.

The beans.xml file can be empty and its presence serves to trigger the CDI container to scan the archive. In Java EE 7 and in particular CDI 1.1, the presence of beans.xml is mandatory. Specifying a bean discovery mode in the XML file can control scanning.

<beans xmlns = "http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation = "http://xmlns.jcp.org/xml/ns/javaee 
  http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
  version = "1.1" bean-discovery-mode = "all">
</beans>

The attribute bean-discovery-mode can have the following values: none, annotated, and all. The default value is annotated, which informs the CDI constructor to scan for all the annotated beans and dependencies; all means consider every object as a possible bean type, and none means do not scan this archive for the CDI bean types.

The beans.xml file determines Alternatives, Interceptors, and Decorators, about which we will learn later.

Factory production

How does CDI handle the situation, where your dependency instance does not have an arguments constructor? Sometimes we want to allow the application control to how and when to instantiate a bean type

CDI deals with factory creation with a feature called producers, which unsurprisingly involves the @Produces annotation. The fully qualified package annotation is called @javax.enterprise.inject.Produces, which informs the CDI container to create an instance of a bean type using the recipient method instead of instantiating the dependency by invoking its no-argument constructor.

Let's look at an example of a factory class as follows:

class EurasianTealAirline implements Airline {
  /* ... */
}

public class AirlineProducer {
  @Produces @Premium
  public Airline connectAirline() {
    return new EurasianAirline();
  }
}

The class AirlineProducer acts as a factory for the Airline components, which actually are the EurasianAirline types, and notice, it is also a premium business connection.

Traditionally, a factory is used to demarcate different type a of bean types in order to delay until runtime the type of object created because the calling code does not know the exact type of dependency. Usually, an enumerated value is passed to the factory in code or during external configuration. With CDI, we use special custom annotation for this.

It is instructional to see the qualifier annotation for the @Premium as follows:

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

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

We define a custom annotation with a runtime retention policy, which can be declared on the class, at a method call, before a type field, and in the method argument positions.

Given these definitions, we can use CDI to inject a specific dependency in a bean and/or service that we are using. If we have a Java-standalone application, a web frontend specifically for customers that only fly with the premium airline services, we would write something like the following:

public class FrontEndPremiumUI extends WebFrontController {
  @Inject @Premium private Airline luxuryService;
  
  public void handleForm(HttpRequest request, HttpResponse response, FormModel form) {
    luxuryService.reserve()
    
    /* ... */
    
    renderView(request, response);
  }
  
  /* ... */
}

The key line of this user interface code is as the following:

@Inject @Premium private Airline luxuryService;

The line instructs the CDI container to inject a dependency of the Airline bean type that is specifically qualified as a @Premium into the bean FrontEndPremiumUI. The CDI container searches for a matching bean type and finds the factory AirlineProducer. It then invokes the method connectAirline(), which returns an instance of EurasianTealAirline.

There is an alternative to define a producer using the initialization method to construct an accessible field. The producer field is the value that will be injected into the targets.

public class AirlineProducerAlternative {
  @Produces @Premium Airline airline;
  
  @Inject
  public void initialize(SpecialContext ctx) {
    airline = new EurasianTealAirline(ctx);
  }
}

Inside the class AirlineProducerAlternative, we inform the CDI container that the field airline is initialized by the factory. In our application, we call the method initialize() in order to create a EurasianTealAirline component with a component SpecialContext that is also injected in.

The CDI container manages the lifecycle of the beans and adds contextual information to it. This means that the instances will be shared by other threads and components managed by the same CDI container.

We do not have to create and use qualifier for simple POJOs that have no discriminating bean types. The CDI container has a default qualifier for bean types. The annotation is called @javax.enterprise.inject.Default.

Generating new instances every time

Sometimes injecting a shared instance from the CDI container is not the behavior that your target bean requires. There are sometimes security issues, concurrency behaviors, and other object safety reasons where a new instance is required.

A new instance can be forced from CDI by using the @New annotation in a producer.

public class AirlineProducer {
  @Produces 
  public Airline connectAirline(@New Airline airline) {
    return new EurasianAirline(airline);
  }
}

This @New annotation is very much like the prototype scope behavior as seen in Spring Framework.

Bean names and presentation views

There is another way to distinguish the CDI beans and that is to optionally give a bean a name. The main reason you would want to do this is to permit a bean to be referenced from the presentation view framework such as JSF. (A full chapter on JSF is out of scope for this book.)

To make a bean accessible through EL, use the @Named built-in qualifier.

@Inject @Named("longTermProvider") @LongTermCredit
CreditProvider guiltsProvider;

If you do not provide the name of the bean, then the @Named qualifier allows you to access the bean by using the name, with the first letter in lowercase. The following two injection points are equivalent in terms of referencing the same CDI bean by name.

class Donkey {  }

class DonkeyRider {
  @Inject @Named donkey donkey1;
  @Inject @Named('donkey') donkey2;
}

Both field variables donkey1 and donkey2 will reference the same CDI bean, provided that the CDI bean is not annotated with @New, of course.

To access the long-term credit bean through JSF, we would write something like the following inside a JSF Facelet view:

<div id = "promoArea">
  <h1>Amazing Credit Offer!</h2>
  <p>Only available to 30<sup>th</sup> September 2014</p>
  <h:form>
    <h:inputText value = "#{applyForm.quote}" title = "quotation value"/>
    <h:inputText value = "#{applyForm.term}" title = "length of text"/>
    <h:commandButton value = "Apply Now!" id = "offerSubmitButton" action = "#{longTermProvider.applyForCredit}"/>
  </h:form>
</div>

This extraction of complicated JSF Facelet illustrates how a named CDI bean type is accessed in the <h:commandButton> tag. A customer clicking on the button invokes the method applyForCredit() on the CDI bean type.

Let's move onwards to the built-in CDI scopes. Illustration of the airline reservation system with several bean types' associated scopes is shown in the following screenshot:

Bean names and presentation views

Bean scopes

In the CDI container there are five predefined scopes, and all of them apart from @Dependent are associated with a bean to provide contextual information. The scopes have different lifetimes and thus different durations.

Scope

Annotation

Duration

Request

@RequestScoped

The beans declared against the request scope live only in existence for the lifetime of HTTP Servlet Request, invocation of remote EJB, a delivery of message to a Message Driven Bean, or a web service endpoint. After the request has been serviced, the bean is destroyed.

Session

@SessionScoped

The beans declared against the session scope live for the lifetime of HTTP session, which is a user's interaction with a web application across multiple HTTP requests. The session scoped beans are only destroyed when the same HTTP session associated with them is destroyed.

Application

@ApplicationScoped

The beans declared against the application scope live for the lifetime of all users' interactions with a web application. In other words, the beans live as long as the web application executes in the managed web container and therefore, the CDI container. Only when the application is destroyed (or undeployed) are the associated application scope beans destroyed.

Dependent

@Dependent

The beans declared against the dependent scope are never shared between the injection points. The injection of a dependent bean lives as long as the lifecycle of the target object to which they are bound. In other words, the CDI container does not manage the dependent beans.

Conversation

@ConversationScoped

The beans declared against the conversation scope are shared across multiple requests in the same HTTP session as long as they associate with the active conversation state. Once the beans fall out of the active connection state (workflow) they are primed for destruction by the CDI container.

For the historical record, the first three scopes are defined by JSR-299 CDI and the JSF API. The last two scopes are only defined by JSR-299.

Advanced Java EE developers can also extend and implement the custom scopes. In order to do so, new scope must declare with the @javax.inject.Scope annotation or @javax.enterprise.context.NormalScope meta-annotation. Beginners in CDI should stay clear of the extensions until they have gained suitable experience.

Note

It is recommended that the developers use @javax.inject.Inject annotation as much as possible, especially for EJB references, over @javax.ejb.EJB. Try it!

CDI initialization and destruction

The applications can register the post-construction and pre-destruction callbacks on the CDI bean types.

The @PostConstruct annotation

When the CDI bean is created, it is associated with a scope. It is possible to register a lifecycle callback method that the CDI framework should call after dependency injection, but also before the class is put into the service.

First, in the managed bean or in any of its superclasses, you define a callback method, which performs the custom initialization. Second, annotate the method with the @javax.annotation.PostConstruct annotation.

class DVLA {
  static String generateKey() {/* ... */}
  static void clearAndReset(String key) {/* ... */}
}

class VehicleRegistration {
  private String dvlaKey;
  private String registration;
  
  @PostConstruct
  public void resetValues() {
    registration = "";
    dvlaKey = DVLA.generateKey("SWANSEA")
  }
}

The CDI container will call the resetValues() method in this bean type VehicleRegistration after all the injections have occurred; therefore, the bean has been fully wired after all the other initializers have been invoked. Sometimes an application wants to apply a look-up dependency to a third-party library as a late binding, and associating the component in the constructor is not appropriate.

The @PreDestroy annotation

When the CDI bean is created, it is associated with a scope. It is possible to register a lifecycle callback method that the CDI framework should call after dependency injection, but before the class is put into the service.

First, in the managed bean or in any of its superclasses, you define a callback method, which performs the custom initialization. Second, annotate the method with the @javax.annotation.PreDestroy annotation.

class DVLA {/* ... */}

class VehicleRegistration {
  private String dvlaKey;
  private String registration;
..
  @PreDestroy
  public void releaseCache() {
    DVLA.clearAndReset(registration);
  }
}

The CDI framework will invoke the @PreDestroy method before the bean type goes out of the contextual scope.

Programmatic Lookup of the CDI Beans

Although the CDI container follows the Hollywood Agency Principle, there is a way to retrieve a bean instance directly. The developers can programmatically ask for an instance of the bean type, to deal with special cases.

public class DVLARegistrationCentre {
  @Inject Instance<DVLA> dvla;
  public DVLA getDVLA() {
    return dvla.get();
  }
}

The @javax.enterprise.inject.Instance annotation allows the application to dynamically obtain the instances of the beans with a specified combination of qualifiers.

The class DVLARegistrationCenter has a method to retrieve the DVLA instance and the instance that is returned is decided by the designation. The get() method of the instance produces a contextual instance of the bean.

Qualifiers can be specified at the injection point with annotations.

@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Police { }

@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Secure { }

public class DVLARegistrationCentre {
  @Inject @Police @Secure Instance<DVLA> dvla;
  public DVLA getDVLA() {
    return dvla.get();
  }
}

In this case, we obtain a different Driver Vehicle Licensing Authority type specific to the UK civil government service and hopefully it is secure.

Advanced readers will notice that this looks extremely similar to the Spring Framework retrieval of the Spring bean from that dependency injection framework. The difference here is that the lookup is provided by the annotations, and therefore is strong typed, not by the name of the bean.

What types of special cases could there be?

  • Instance lookup is useful if the qualifiers can vary dynamically at runtime
  • We want to iterate over all the beans of a certain type
  • We want to provide a fallback method, where there is no bean that satisfies the type and the set of qualifiers

The following is an example to iterate all the beans in the CDI container of a specified type:

class DVLA {
  public void init() {/*...*/}
  /* ... */
}

@Inject
void initRegistries(@Any Instance<DVLA> registries) {
  for (DVLA registry: registries) {
    registry.init();
  }
}

We use the @Any qualifier to override the @Default annotation in order to remove the restriction of the bean type suitable for injection. Remember, the @Any qualifier says that you are declaring an injection point, where you do care about contextual information of the CDI bean.

Configuring a CDI application

It is very easy to configure a CDI application. All you need to do is provide a file called beans.xml, which must be found on the classpath. The file can be completely empty. For web applications, the standard dictates that the beans.xml file must live in the WEB-INF directory. For the EJB modules or the JAR files must live in the META-INF directory.

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

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