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.
ClassCastException
at the execution time.The following diagram illustrates the built-in contextual scopes inside the CDI container:
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:
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:
java.lang
.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:
@Decorator
ejb-jar.xml
Let us summarize some of these definitions into a handy reference as follows:
Term |
Definition |
---|---|
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. | |
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. | |
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. | |
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. | |
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. | |
All CDI beans by definition provide an implementation of the types that define them. Implementations are typically in a class. |
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.
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.
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.
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.
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.
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 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(); } }
Here is a table of the built-in qualifiers in Context and Dependency Injection.
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.
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
.
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.
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:
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 |
---|---|---|
|
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. | |
|
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. | |
|
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. | |
|
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. | |
|
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.
The applications can register the post-construction and pre-destruction callbacks on the CDI bean types.
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.
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.
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?
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.
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.
18.117.75.70