CDI (Contexts and Dependency Injection) is a set of services in Java EE (now Jakarta EE) that enables the management of object lifecycles and dependencies within an application. It allows developers to decouple code and make it more modular and testable by using dependency injection (DI), and it manages the contexts in which objects are created, injected, and destroyed.
CDI provides a way to:
- Inject dependencies into objects automatically, without needing to manage them manually.
- Use contexts to manage the lifecycle of objects (such as request, session, or application).
- Offer powerful features such as interceptors, decorators, and events to enhance the behavior of beans in a standardized way.
1. Core Concepts of CDI
1.1. Dependency Injection (DI)
Dependency Injection is a design pattern that removes the need for objects to manage their own dependencies. CDI manages the dependencies between classes, automatically injecting them at runtime based on the context.
- Injection Types:
- Field Injection: Injects dependencies directly into fields of a class.
- Constructor Injection: Dependencies are provided via the constructor.
- Setter Injection: Dependencies are injected through setter methods.
For example, in CDI, you can inject an instance of one class into another:
import javax.inject.Inject;
public class Car {
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.run();
}
}
In this example, Car
depends on Engine
, and CDI automatically provides the appropriate Engine
instance.
1.2. Bean Types
In CDI, a bean is any object that can be managed by the CDI container. There are different types of beans, including:
- Managed Beans: The simplest form of beans in CDI. They are automatically created, initialized, and injected by the CDI container.
- Enterprise Beans (EJB): These are special beans that are part of Enterprise JavaBeans but are also managed by CDI.
- Session Beans: Beans that can hold state and manage business logic in a container.
CDI automatically manages these beans by injecting dependencies based on context and bean type.
1.3. Contexts
CDI introduces the concept of contexts, which define the lifecycle of beans in relation to certain scopes, such as:
- Request Context: Beans are created and destroyed with each HTTP request.
- Session Context: Beans are created and live as long as a user session.
- Application Context: Beans live for the entire duration of the application.
This helps manage the scope of beans and ensures they exist only when necessary.
1.4. Qualifiers
CDI allows developers to qualify which beans to inject when multiple beans of the same type exist. Qualifiers provide more specificity about which bean to inject by adding metadata to beans.
For example:
@Qualifier
@Retention(RUNTIME)
@Target({ FIELD, METHOD, PARAMETER, TYPE })
public @interface EngineType {
String value();
}
@Inject
@EngineType("V8")
private Engine engine;
Here, the EngineType
qualifier is used to inject the specific Engine
bean that has a qualifier value of “V8”.
2. CDI Annotations
CDI uses annotations to define how beans are created, injected, and managed. Some of the most commonly used CDI annotations are:
2.1. @Inject
The @Inject
annotation is used to declare that a field, constructor, or method should be injected with a dependency by the CDI container.
@Inject
private Engine engine;
2.2. @Named
The @Named
annotation allows a bean to be referred to by name in the injection process. It’s often used for simpler integration with technologies like JSF or JSP.
@Named("myEngine")
public class Engine {
// Engine implementation
}
2.3. @Qualifier
@Qualifier
is used to define a custom annotation that can be applied to beans, allowing developers to specify which beans to inject when there are multiple candidates of the same type.
@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, FIELD, PARAMETER })
public @interface DieselEngine {
}
2.4. @Scope
CDI provides predefined scopes to control the lifecycle of beans. Some common scopes are:
@RequestScoped
: Bean exists only for the duration of a single HTTP request.@SessionScoped
: Bean lives for the duration of the user session.@ApplicationScoped
: Bean is created once and shared across the application.
@RequestScoped
public class UserService {
// Business logic for handling user data
}
2.5. @Produces
@Produces
is used to define how an object is created for injection. This is particularly useful when you need to produce complex objects that require some logic to instantiate.
@Produces
public Engine createEngine() {
return new V8Engine();
}
This tells CDI to produce a V8Engine
instance whenever Engine
is injected.
3. Advanced CDI Features
3.1. Interceptors
Interceptors allow you to intercept method calls and modify behavior before or after they are invoked. They are often used for cross-cutting concerns such as logging, transactions, or security checks.
@Interceptor
@Logging
public class LoggingInterceptor {
@AroundInvoke
public Object logMethodInvocation(InvocationContext context) throws Exception {
System.out.println("Logging: " + context.getMethod().getName());
return context.proceed();
}
}
3.2. Decorators
Decorators are used to wrap or enhance the behavior of other beans. They are similar to interceptors but are focused on modifying bean behavior rather than intercepting method calls.
@Decorator
public class CarDecorator implements Car {
@Inject
private Car car;
@Override
public void start() {
System.out.println("Decorator: Starting car with additional features");
car.start();
}
}
3.3. Events
CDI supports event-based programming. You can fire events and have interested beans handle those events asynchronously.
@Inject
private Event<MyEvent> event;
public void triggerEvent() {
event.fire(new MyEvent("Event data"));
}
@Observer
public void onEvent(MyEvent event) {
System.out.println("Event received: " + event.getData());
}
4. CDI in Java EE vs. Jakarta EE
CDI has been a part of Java EE and is now an integral part of Jakarta EE. Jakarta EE carries forward CDI with improved support for modern architectures such as microservices and reactive programming.
5. CDI Use Case Example
Here’s a simple CDI example with an injectable service and different bean scopes:
5.1. Service Interface
public interface CarService {
void serviceCar();
}
5.2. Service Implementation
@Named
@RequestScoped
public class CarServiceImpl implements CarService {
@Override
public void serviceCar() {
System.out.println("Car is being serviced.");
}
}
5.3. Using CDI in a Controller
@Named
@RequestScoped
public class CarController {
@Inject
private CarService carService;
public void serviceCar() {
carService.serviceCar();
}
}