| name | java-cdi |
| description | Core CDI patterns including constructor injection, scopes, producers, and container configuration |
| user-invocable | false |
| allowed-tools | Read, Edit, Write, Bash, Grep, Glob |
Java CDI Skill
Core CDI (Contexts and Dependency Injection) standards applicable to any CDI container. This skill covers dependency injection patterns, scopes, and producer methods.
Prerequisites
This skill applies to Jakarta CDI projects:
jakarta.inject:jakarta.inject-api
jakarta.enterprise:jakarta.enterprise.cdi-api
Required Imports
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.context.SessionScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.Instance;
import org.eclipse.microprofile.config.inject.ConfigProperty;
References
Constructor Injection (Mandatory)
REQUIRED: Always use constructor injection instead of field injection.
For foundational constructor injection principles (immutability, testability, fail-fast behavior), see pm-dev-java:java-core skill.
Single Constructor Rule
When a CDI bean has exactly one constructor, CDI automatically treats it as the injection point - no @Inject needed:
@ApplicationScoped
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
public OrderService(PaymentService paymentService,
InventoryService inventoryService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}
}
Multiple Constructors Rule
When a CDI bean has multiple constructors, you MUST explicitly mark the injection constructor with @Inject:
@ApplicationScoped
public class ConfigurableService {
private final DatabaseService databaseService;
private final String configValue;
public ConfigurableService() {
this.databaseService = null;
this.configValue = "default";
}
@Inject
public ConfigurableService(DatabaseService databaseService,
@ConfigProperty(name = "app.config") String configValue) {
this.databaseService = databaseService;
this.configValue = configValue;
}
}
Anti-Patterns
@Inject
private UserService userService;
@Inject
public void setUserService(UserService userService) {
this.userService = userService;
}
CDI Scopes
| Scope | Lifecycle | Use Case |
|---|
@ApplicationScoped | Single instance per application | Stateless services, most business logic |
@RequestScoped | New instance per HTTP request | Request-specific data |
@SessionScoped | New instance per HTTP session | User session data |
@Dependent | New instance per injection | Helpers, utilities |
@Singleton | Single instance (eager init) | Use sparingly, prefer @ApplicationScoped |
@ApplicationScoped
public class UserService { }
@RequestScoped
public class RequestContext { }
Optional Dependencies
Use Instance<T> when a dependency might not be available:
@ApplicationScoped
public class NotificationService {
private final EmailService emailService;
private final SmsService smsService;
public NotificationService(EmailService emailService,
Instance<SmsService> smsServiceInstance) {
this.emailService = emailService;
this.smsService = smsServiceInstance.isResolvable() ?
smsServiceInstance.get() : null;
}
public void sendNotification(String message) {
emailService.send(message);
if (smsService != null) {
smsService.send(message);
}
}
}
Producer Methods
Scope and Null Return Rules
CRITICAL: CDI has strict rules about producer methods returning null.
| Scope | Can Return Null? | Reason |
|---|
@Dependent | ✅ Yes | No proxy needed |
@RequestScoped | ❌ No | Proxy requires target object |
@SessionScoped | ❌ No | Proxy requires target object |
@ApplicationScoped | ❌ No | Proxy requires target object |
@Dependent Scope (Allows null)
@ApplicationScoped
public class ServletObjectsProducer {
@Produces
@Dependent
public HttpServletRequest produceHttpServletRequest() {
return getHttpServletRequest().orElse(null);
}
}
Normal Scoped Producers (Cannot return null)
@Produces
@RequestScoped
public SomeService createService() {
return null;
}
@Produces
@RequestScoped
public NotificationService createNotificationService() {
return notificationEnabled ?
new EmailNotificationService() :
new NoOpNotificationService();
}
Recommended Patterns for Optional Dependencies
- Use Instance at Injection Points (preferred)
- Use @Dependent Scope with Null Returns
- Use Null Object Pattern
AVOID: Returning Optional<T> from producer methods - goes against CDI design philosophy.
Error Handling
Common CDI Issues
| Problem | Exception | Solution |
|---|
| Missing dependency | UnsatisfiedResolutionException | Ensure dependency is a CDI bean with appropriate scope |
| Multiple implementations | AmbiguousResolutionException | Use @Named or custom qualifiers |
| Circular dependencies | DeploymentException | Refactor architecture or use Instance<T> for lazy init |
@ApplicationScoped
public class PaymentService {
public PaymentService(@Named("primary") PaymentGateway gateway) {
}
}
Quality Checklist
Related Skills
pm-dev-java:java-cdi-quarkus - Quarkus-specific CDI patterns, container/Docker config, security
pm-dev-java:java-core - Core Java patterns
pm-dev-java:junit-core - CDI testing patterns