All Java templates

Jakarta CDI (Contexts & Dependency Injection)

CDI scopes, qualifiers, producers, events, and the lifecycle to actually understand.

DevZone Tools1,180 copiesUpdated Mar 13, 2026Jakarta EEJava
# CLAUDE.md — Jakarta CDI

## Scopes

- `@ApplicationScoped` — one instance for the whole app. Use for stateless services.
- `@RequestScoped` — one per HTTP request. Use for request-tied state.
- `@SessionScoped` — one per HTTP session. Avoid in stateless APIs.
- `@Dependent` — same lifecycle as the consumer. Default for unannotated beans.
- `@Singleton` is **not the same** as `@ApplicationScoped` — `@Singleton` skips proxying. Stick with `@ApplicationScoped` unless you measure.

## Injection

- Constructor injection over field injection:
  ```java
  @ApplicationScoped
  public class CheckoutService {
      private final OrderRepository orders;
      private final PaymentGateway payments;
      @Inject CheckoutService(OrderRepository orders, PaymentGateway payments) {
          this.orders = orders;
          this.payments = payments;
      }
  }
  ```
- Field injection (`@Inject` on a field) is allowed but harder to test and hides dependencies.
- Don't inject in `@PostConstruct` — too late; bean state is already half-initialized.

## Qualifiers

- When you have multiple beans of the same type, use a **qualifier annotation**:
  ```java
  @Qualifier @Retention(RUNTIME) @Target({FIELD, PARAMETER})
  public @interface Stripe {}
  ```
- Inject with `@Inject @Stripe PaymentGateway gateway`.
- Don't use `@Named("stripe")` for non-EL contexts. Qualifiers are type-checked; names are strings.

## Producers

- For third-party types you can't annotate, write a `@Produces` method:
  ```java
  @Produces @ApplicationScoped
  HttpClient httpClient() { return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build(); }
  ```
- Producer methods can take parameters that themselves get injected.
- For cleanup, write a `@Disposes` method.

## Events

- CDI events decouple producers from consumers:
  ```java
  @Inject Event<UserCreated> userCreated;
  userCreated.fire(new UserCreated(user.id()));
  ```
- Observers: `void on(@Observes UserCreated event) { ... }`.
- For async: `userCreated.fireAsync(new UserCreated(...))`. Observer signature: `void on(@ObservesAsync UserCreated event)`.
- Don't make synchronous observers do slow work. They block the firing thread.

## Interceptors

- For cross-cutting concerns (logging, metrics, transactions). Built into the platform.
- Define an `@InterceptorBinding` annotation, an `@Interceptor` class, and apply the binding to methods.
- For most observability, **MicroProfile** + Jakarta EE interceptors give you what you need.

## Lifecycle hooks

- `@PostConstruct` — called once after injection completes. Use for initialization.
- `@PreDestroy` — called before the bean is destroyed. Use for cleanup.
- These hooks must not throw.

## Don't

- Don't put state in `@ApplicationScoped` beans without synchronization.
- Don't use `@Inject` to inject `@RequestScoped` beans into `@ApplicationScoped` ones unless you understand the proxy semantics.
- Don't use `BeanManager.getBeans` to look up beans dynamically. That's the service-locator anti-pattern.
- Don't put mutable static fields on CDI beans.

Other Java templates