Claude-skill-registry java-cdi
Core CDI patterns including constructor injection, scopes, producers, and container configuration
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/java-cdi" ~/.claude/skills/majiayu000-claude-skill-registry-java-cdi && rm -rf "$T"
skills/data/java-cdi/SKILL.mdJava 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-apijakarta.enterprise:jakarta.enterprise.cdi-api
Required Imports
// CDI Core import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.inject.Singleton; // CDI Scopes import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.SessionScoped; import jakarta.enterprise.context.Dependent; // CDI Producers and Optional Dependencies import jakarta.enterprise.inject.Produces; import jakarta.enterprise.inject.Instance; // Quarkus Configuration 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; // No @Inject needed - only one constructor 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 // REQUIRED - multiple constructors exist public ConfigurableService(DatabaseService databaseService, @ConfigProperty(name = "app.config") String configValue) { this.databaseService = databaseService; this.configValue = configValue; } }
Anti-Patterns
// ❌ Field Injection - FORBIDDEN @Inject private UserService userService; // ❌ Setter Injection - FORBIDDEN @Inject public void setUserService(UserService userService) { this.userService = userService; }
CDI Scopes
| Scope | Lifecycle | Use Case |
|---|---|---|
| Single instance per application | Stateless services, most business logic |
| New instance per HTTP request | Request-specific data |
| New instance per HTTP session | User session data |
| New instance per injection | Helpers, utilities |
| Single instance (eager init) | Use sparingly, prefer @ApplicationScoped |
@ApplicationScoped public class UserService { } // Singleton across application @RequestScoped public class RequestContext { } // New instance per HTTP request
Optional Dependencies
Use
Instance<T> when a dependency might not be available:
@ApplicationScoped public class NotificationService { private final EmailService emailService; private final SmsService smsService; // May be null 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); // Always available if (smsService != null) { smsService.send(message); // Optional } } }
Producer Methods
Scope and Null Return Rules
CRITICAL: CDI has strict rules about producer methods returning null.
| Scope | Can Return Null? | Reason |
|---|---|---|
| ✅ Yes | No proxy needed |
| ❌ No | Proxy requires target object |
| ❌ No | Proxy requires target object |
| ❌ No | Proxy requires target object |
@Dependent Scope (Allows null)
@ApplicationScoped public class ServletObjectsProducer { @Produces @Dependent // ✅ REQUIRED for null returns public HttpServletRequest produceHttpServletRequest() { return getHttpServletRequest().orElse(null); // Safe with @Dependent } }
Normal Scoped Producers (Cannot return null)
// ❌ ILLEGAL - will throw IllegalProductException @Produces @RequestScoped public SomeService createService() { return null; // CDI will throw exception at runtime } // ✅ CORRECT - Use Null Object pattern @Produces @RequestScoped public NotificationService createNotificationService() { return notificationEnabled ? new EmailNotificationService() : new NoOpNotificationService(); // Never null }
Recommended Patterns for Optional Dependencies
- Use Instance<T> 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 | | Ensure dependency is a CDI bean with appropriate scope |
| Multiple implementations | | Use or custom qualifiers |
| Circular dependencies | | Refactor architecture or use for lazy init |
// Disambiguate with @Named @ApplicationScoped public class PaymentService { public PaymentService(@Named("primary") PaymentGateway gateway) { // Uses specifically qualified implementation } }
Quality Checklist
- Constructor injection used (never field/setter injection)
- Final fields for all injected dependencies
- Single constructor (no @Inject needed) or @Inject on injection constructor
- Appropriate scope selected for each bean
- Instance<T> used for optional dependencies
- Producer methods use @Dependent if returning null
- Normal-scoped producers never return null
Related Skills
- Quarkus-specific CDI patterns, container/Docker config, securitypm-dev-java:java-cdi-quarkus
- Core Java patternspm-dev-java:java-core
- CDI testing patternspm-dev-java:junit-core