Introduction to Jakarta Contexts and Dependency Injection
We are working on a fresh, updated Jakarta EE Tutorial. This section hasn’t yet been updated. |
This chapter describes Jakarta Contexts and Dependency Injection (CDI) which is one of several Jakarta EE features that help to knit together the web tier and the transactional tier of the Jakarta EE platform.
Getting Started
Contexts and Dependency Injection (CDI) enables your objects to have their dependencies provided to them automatically, instead of creating them or receiving them as parameters. CDI also manages the lifecycle of those dependencies for you.
For example, consider the following servlet:
@WebServlet("/cdiservlet")
public class NewServlet extends HttpServlet {
private Message message;
@Override
public void init() {
message = new MessageB();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().write(message.get());
}
}
This servlet needs an instance of an object that implements the Message
interface:
public interface Message {
public String get();
}
The servlet creates itself an instance of the following object:
public class MessageB implements Message {
public MessageB() { }
@Override
public String get() {
return "message B";
}
}
Using CDI, this servlet can declare its dependency on a Message
instance and have it injected automatically by the CDI runtime.
The new servlet code is the following:
@WebServlet("/cdiservlet")
public class NewServlet extends HttpServlet {
@Inject private Message message;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().write(message.get());
}
}
The CDI runtime looks for classes that implement the Message
interface, finds the MessageB
class, creates a new instance of it, and injects it into the servlet at runtime.
To manage the lifecycle of the new instance, the CDI runtime needs to know what the scope of the instance should be.
In this example, the servlet only needs the instance to process an HTTP request; the instance can then be garbage collected.
This is specified using the jakarta.enterprise.context.RequestScoped
annotation:
@RequestScoped
public class MessageB implements Message { ... }
For more information on scopes, see Using Scopes.
The MessageB
class is a CDI bean.
CDI beans are classes that CDI can instantiate, manage, and inject automatically to satisfy the dependencies of other objects.
Almost any Java class can be managed and injected by CDI.
For more information on beans, see About Beans.
A JAR or WAR file that contains a CDI bean is a bean archive.
For more information on packaging bean archives, see Configuring a CDI Application in this chapter and Packaging CDI Applications in Jakarta Contexts and Dependency Injection: Advanced Topics.
In this example, MessageB
is the only class that implements the Message
interface.
If an application has more than one implementation of an interface, CDI provides mechanisms that you can use to select which implementation to inject.
For more information, see Using Qualifiers in this chapter and Using Alternatives in CDI Applications in Jakarta Contexts and Dependency Injection: Advanced Topics.
Overview of CDI
CDI is a set of services that, used together, make it easy for developers to use enterprise beans along with Jakarta Faces technology in web applications. Designed for use with stateful objects, CDI also has many broader uses, allowing developers a great deal of flexibility to integrate various kinds of components in a loosely coupled but typesafe way.
CDI 3.0 is specified in a Jakarta EE specification. Related specifications that CDI uses include the following:
-
Jakarta Dependency Injection
-
The Managed Beans specification, an offshoot of the Jakarta EE platform specification
The most fundamental services provided by CDI are as follows.
-
Contexts: This service enables you to bind the lifecycle and interactions of stateful components to well-defined but extensible lifecycle contexts.
-
Dependency injection: This service enables you to inject components into an application in a typesafe way and to choose at deployment time which implementation of a particular interface to inject.
In addition, CDI provides the following services:
-
Integration with the Expression Language (EL), which allows any component to be used directly within a Jakarta Faces page or a Jakarta Server Pages page
-
The ability to decorate injected components
-
The ability to associate interceptors with components using typesafe interceptor bindings
-
An event-notification model
-
A web conversation scope in addition to the three standard scopes (request, session, and application) defined by the Jakarta Servlet specification
-
A complete Service Provider Interface (SPI) that allows third-party frameworks to integrate cleanly in the Jakarta EE environment
A major theme of CDI is loose coupling. CDI does the following:
-
Decouples the server and the client by means of well-defined types and qualifiers, so that the server implementation may vary
-
Decouples the lifecycles of collaborating components by
-
Making components contextual, with automatic lifecycle management
-
Allowing stateful components to interact like services, purely by message passing
-
-
Completely decouples message producers from consumers, by means of events
-
Decouples orthogonal concerns by means of Jakarta EE interceptors
Along with loose coupling, CDI provides strong typing by
-
Eliminating lookup using string-based names for wiring and correlations so that the compiler will detect typing errors
-
Allowing the use of declarative Java annotations to specify everything, largely eliminating the need for XML deployment descriptors, and making it easy to provide tools that introspect the code and understand the dependency structure at development time
CDI Lite vs CDI Full
As of CDI version 4.0 in Jakarta EE version 10, the Core CDI functionality has been split into two parts: CDI Lite and CDI Full.
CDI Lite provides a subset of the CDI Full functionality with an emphasis on build-time implementations. By leaving out the additional components from CDI Full such as those dealing with runtime reflection, CDI Lite is able to execute in lighter and more restricted environments.
Jakarta EE-compliant application servers will still implement the CDI Full functionality so this change will benefit those developers working in alternate (e.g. cloud-based) environments without affecting those working in a standard Jakarta EE environment.
Functionality available only in CDI Full includes the following:
-
Binding interceptors using @Interceptors
-
Explicit bean archives
-
Declaring interceptors on classes using @AroundInvoke
-
Decorator classes
-
Portable extensions
-
Serialization via passivation/activation
-
Session scope, conversation scope
-
Specialization using @Alternative and @Specializes
Please see the Further Information about CDI section of this chapter for links to the latest specification.
The remainder of this chapter deals with the CDI Lite profile. The tutorial chapter on CDI Full can be found here. |
About Beans
CDI redefines the concept of a bean beyond its use in other Java technologies, such as the JavaBeans and Jakarta Enterprise Beans technologies. In CDI, a bean is a source of contextual objects that define application state or logic. A Jakarta EE component is a bean if the lifecycle of its instances may be managed by the container according to the lifecycle context model defined in the CDI specification.
More specifically, a bean has the following attributes:
-
A (nonempty) set of bean types
-
A (nonempty) set of qualifiers (see Using Qualifiers)
-
A scope (see Using Scopes)
-
Optionally, a bean EL name (see Giving Beans EL Names)
-
A set of interceptor bindings
-
A bean implementation
A bean type defines a client-visible type of the bean. Almost any Java type may be a bean type of a bean.
-
A bean type may be an interface, a concrete class, or an abstract class and may be declared final or have final methods.
-
A bean type may be a parameterized type with type parameters and type variables.
-
A bean type may be an array type. Two array types are considered identical only if the element type is identical.
-
A bean type may be a primitive type. Primitive types are considered to be identical to their corresponding wrapper types in
java.lang
. -
A bean type may be a raw type.
About CDI Managed Beans
A managed bean is implemented by a Java class, which is called its bean class. A top-level Java class is a managed bean if it is defined to be a managed bean by any other Jakarta EE technology specification, such as the Jakarta Faces technology specification, or if it meets all the following conditions.
-
It is not a nonstatic inner class.
-
It is a concrete class or is annotated
@Decorator
. -
It is not annotated with an enterprise bean component-defining annotation or declared as an enterprise bean class in
ejb-jar.xml
. -
It has an appropriate constructor. That is, one of the following is the case.
-
The class has a constructor with no parameters.
-
The class declares a constructor annotated
@Inject
.
-
No special declaration, such as an annotation, is required to define a managed bean.
Beans as Injectable Objects
The concept of injection has been part of Java technology for some time. Since the Java EE 5 platform was introduced, annotations have made it possible to inject resources and some other kinds of objects into container-managed objects. CDI makes it possible to inject more kinds of objects and to inject them into objects that are not container-managed.
The following kinds of objects can be injected:
-
Almost any Java class
-
Session beans
-
Jakarta EE resources: data sources, Messaging topics, queues, connection factories, and the like
-
Persistence contexts (Jakarta Persistence
EntityManager
objects) -
Producer fields
-
Objects returned by producer methods
-
Web service references
-
Remote enterprise bean references
For example, suppose that you create a simple Java class with a method that returns a string:
package greetings;
public class Greeting {
public String greet(String name) {
return "Hello, " + name + ".";
}
}
This class becomes a bean that you can then inject into another class. This bean is not exposed to the EL in this form. Giving Beans EL Names explains how you can make a bean accessible to the EL.
Using Qualifiers
You can use qualifiers to provide various implementations of a particular bean type.
A qualifier is an annotation that you apply to a bean.
A qualifier type is a Java annotation defined as @Target({METHOD, FIELD, PARAMETER, TYPE})
and @Retention(RUNTIME)
.
For example, you could declare an @Informal
qualifier type and apply it to another class that extends the Greeting
class.
To declare this qualifier type, use the following code:
package greetings;
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 static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import jakarta.inject.Qualifier;
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Informal {}
You can then define a bean class that extends the Greeting
class and uses this qualifier:
package greetings;
@Informal
public class InformalGreeting extends Greeting {
public String greet(String name) {
return "Hi, " + name + "!";
}
}
Both implementations of the bean can now be used in the application.
If you define a bean with no qualifier, then the bean automatically has the qualifier @Default
.
The unannotated Greeting
class could be declared as follows:
package greetings;
import jakarta.enterprise.inject.Default;
@Default
public class Greeting {
public String greet(String name) {
return "Hello, " + name + ".";
}
}
Injecting Beans
To use the beans you create, you inject them into yet another bean that can then be used by an application, such as a Jakarta Faces application.
For example, you might create a bean called Printer
into which you would inject one of the Greeting
beans:
import jakarta.inject.Inject;
public class Printer {
@Inject Greeting greeting;
...
}
This code injects the @Default
Greeting
implementation into the bean.
The following code injects the @Informal
implementation:
import jakarta.inject.Inject;
public class Printer {
@Inject @Informal Greeting greeting;
...
}
More is needed for the complete picture of this bean. Its use of scope needs to be understood. In addition, for a Jakarta Faces application, the bean needs to be accessible through the EL.
Now that you can identify the target of the injection, it is important to understand what can be injected and in what context.
Faces 2.3 and above provides producers that enable most important Faces artifacts to be injected.
For detailed information, see the package javadoc for jakarta.faces.annotation
.
Using Scopes
For a web application to use a bean that injects another bean class, the bean needs to be able to hold state over the duration of the user’s interaction with the application. The way to define this state is to give the bean a scope. You can give an object any of the scopes described in Scopes, depending on how you are using it.
Scope | Annotation | Duration |
---|---|---|
Request |
|
A user’s interaction with a web application in a single HTTP request. |
Session |
|
A user’s interaction with a web application across multiple HTTP requests. |
Application |
|
Shared state across all users' interactions with a web application. |
Dependent |
|
The default scope if none is specified; it means that an object exists to serve exactly one client (bean) and has the same lifecycle as that client (bean). |
Conversation |
|
A user’s interaction with a servlet, including Jakarta Faces applications. The conversation scope exists within developer-controlled boundaries that extend it across multiple requests for long-running conversations. All long-running conversations are scoped to a particular HTTP servlet session and may not cross session boundaries. |
The first three scopes are defined by both Jakarta Context and Dependency Injection and the Jakarta Faces specification. The last two are defined by Jakarta Context and Dependency Injection.
All predefined scopes except @Dependent
are contextual scopes.
CDI places beans of contextual scope in the context whose lifecycle is defined by the Jakarta EE specifications.
For example, a session context and its beans exist during the lifetime of an HTTP session.
Injected references to the beans are contextually aware.
The references always apply to the bean that is associated with the context for the thread that is making the reference.
The CDI container ensures that the objects are created and injected at the correct time as determined by the scope that is specified for these objects.
You can also define and implement custom scopes, but that is an advanced topic. Custom scopes are likely to be used by those who implement and extend the CDI specification.
A scope gives an object a well-defined lifecycle context. A scoped object can be automatically created when it is needed and automatically destroyed when the context in which it was created ends. Moreover, its state is automatically shared by any clients that execute in the same context.
Jakarta EE components, such as servlets and enterprise beans, and JavaBeans components do not by definition have a well-defined scope. These components are one of the following:
-
Singletons, such as enterprise singleton beans, whose state is shared among all clients
-
Stateless objects, such as servlets and stateless session beans, which do not contain client-visible state
-
Objects that must be explicitly created and destroyed by their client, such as JavaBeans components and stateful session beans, whose state is shared by explicit reference passing between clients
However, if you create a Jakarta EE component that is a managed bean, then it becomes a scoped object, which exists in a well-defined lifecycle context.
The web application for the Printer
bean will use a simple request and response mechanism, so the managed bean can be annotated as follows:
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
@RequestScoped
public class Printer {
@Inject @Informal Greeting greeting;
...
}
Beans that use session, application, or conversation scope must be serializable, but beans that use request scope do not have to be serializable.
Giving Beans EL Names
To make a bean accessible through the EL, use the @Named
built-in qualifier:
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
@RequestScoped
public class Printer {
@Inject @Informal Greeting greeting;
...
}
The @Named
qualifier allows you to access the bean by using the bean name, with the first letter in lowercase.
For example, a Facelets page would refer to the bean as printer
.
You can specify an argument to the @Named
qualifier to use a nondefault name:
@Named("MyPrinter")
With this annotation, the Facelets page would refer to the bean as MyPrinter
.
Adding Setter and Getter Methods
To make the state of the managed bean accessible, add setter and getter methods for that state.
The createSalutation
method calls the bean’s greet
method, and the getSalutation
method retrieves the result.
Once the setter and getter methods have been added, the bean is complete. The final code looks like this:
package greetings;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
@RequestScoped
public class Printer {
@Inject @Informal Greeting greeting;
private String name;
private String salutation;
public void createSalutation() {
this.salutation = greeting.greet(name);
}
public String getSalutation() {
return salutation;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Using a Managed Bean in a Facelets Page
To use the managed bean in a Facelets page, create a form that uses user interface elements to call its methods and to display their results. The following example provides a button that asks the user to type a name, retrieves the salutation, and then displays the text in a paragraph below the button:
<h:form id="greetme">
<p><h:outputLabel value="Enter your name: " for="name"/>
<h:inputText id="name" value="#{printer.name}"/></p>
<p><h:commandButton value="Say Hello"
action="#{printer.createSalutation}"/></p>
<p><h:outputText value="#{printer.salutation}"/></p>
</h:form>
Injecting Objects by Using Producer Methods
Producer methods provide a way to inject objects that are not beans, objects whose values may vary at runtime, and objects that require custom initialization.
For example, if you want to initialize a numeric value defined by a qualifier named @MaxNumber
, then you can define the value in a managed bean and then define a producer method, getMaxNumber
, for it:
private int maxNumber = 100;
...
@Produces @MaxNumber int getMaxNumber() {
return maxNumber;
}
When you inject the object in another managed bean, the container automatically invokes the producer method, initializing the value to 100:
@Inject @MaxNumber private int maxNumber;
If the value can vary at runtime, then the process is slightly different.
For example, the following code defines a producer method that generates a random number defined by a qualifier called @Random
:
private java.util.Random random =
new java.util.Random( System.currentTimeMillis() );
java.util.Random getRandom() {
return random;
}
@Produces @Random int next() {
return getRandom().nextInt(maxNumber);
}
When you inject this object in another managed bean, you declare a contextual instance of the object:
@Inject @Random Instance<Integer> randomInt;
You then call the get
method of the Instance
:
this.number = randomInt.get();
Configuring a CDI Application
When your beans are annotated with a scope type, the server recognizes the application as a bean archive and no additional configuration is required. The possible scope types for CDI beans are listed in Using Scopes.
CDI uses an optional deployment descriptor named beans.xml
.
Like other Jakarta EE deployment descriptors, the configuration settings in beans.xml
are used in addition to annotation settings in CDI classes.
The settings in beans.xml
override the annotation settings if there is a conflict.
An archive must contain the beans.xml
deployment descriptor only in certain limited situations, described in Jakarta Contexts and Dependency Injection: Advanced Topics.
For a web application, the beans.xml
deployment descriptor, if present, must be in the WEB-INF
directory.
For EJB modules or JAR files, the beans.xml
deployment descriptor, if present, must be in the META-INF
directory.
Using the @PostConstruct and @PreDestroy Annotations with CDI Managed Bean Classes
CDI managed bean classes and their superclasses support the annotations for initializing and for preparing for the destruction of a bean. These annotations are defined in Jakarta Annotations (https://jakarta.ee/specifications/annotations/2.0/).
To Initialize a Managed Bean Using the @PostConstruct Annotation
Initializing a managed bean specifies the lifecycle callback method that the CDI framework should call after dependency injection but before the class is put into service.
-
In the managed bean class or any of its superclasses, define a method that performs the initialization that you require.
-
Annotate the declaration of the method with the
jakarta.annotation.PostConstruct
annotation.
When the managed bean is injected into a component, CDI calls the method after all injection has occurred and after all initializers have been called.
As mandated in Jakarta Annotations, if the annotated method is declared in a superclass, the method is called unless a subclass of the declaring class overrides the method. |
The UserNumberBean
managed bean in The guessnumber-cdi CDI Example uses @PostConstruct
to annotate a method that resets all bean fields:
@PostConstruct
public void reset () {
this.minimum = 0;
this.userNumber = 0;
this.remainingGuesses = 0;
this.maximum = maxNumber;
this.number = randomInt.get();
}
To Prepare for the Destruction of a Managed Bean Using the @PreDestroy Annotation
Preparing for the destruction of a managed bean specifies the lifecycle call back method that signals that an application component is about to be destroyed by the container.
-
In the managed bean class or any of its superclasses, prepare for the destruction of the managed bean.
In this method, perform any cleanup that is required before the bean is destroyed, such as releasing a resource that the bean has been holding.
-
Annotate the declaration of the method with the
jakarta.annotation.PreDestroy
annotation.
CDI calls this method before starting to destroy the bean.
Further Information about CDI
For more information about CDI, see
-
Jakarta Contexts and Dependency Injection specification:
https://jakarta.ee/specifications/cdi/4.0/ -
Weld - CDI Implementation:
https://docs.jboss.org/weld/reference/latest/en-US/html/ -
Jakarta Dependency Injection specification:
https://jakarta.ee/specifications/dependency-injection/2.0/