A common mistake developers make when working with Jakarta EE applications is mixing up annotation-driven dependency injection mechanisms. For example, using @EJB
where @Inject
is preferred or misconfiguring @Resource
for JNDI lookups. This leads to fragile applications that fail at runtime with vague errors.
Annotations like @EJB
, @Resource
, and @Inject
are cornerstones of enterprise Java development. They enable clean, declarative dependency injection without boilerplate lookup code. Whether you are injecting an EJB, looking up a resource like a DataSource
, or using CDI for managed beans, annotations reduce complexity and let the container handle lifecycle and wiring.
Think of these annotations as different types of service labels in a warehouse: @EJB
for enterprise services, @Resource
for utilities (like database or mail), and @Inject
for CDI beans. Each has its place, but using the wrong one is like labeling a box incorrectly—chaos follows.
Core Annotations
1. @EJB
– Enterprise Java Beans Injection
import jakarta.ejb.EJB;
import jakarta.ejb.Stateless;
@Stateless
public class PaymentService {
public void processPayment() {
System.out.println("Payment processed");
}
}
public class OrderService {
@EJB
private PaymentService paymentService;
public void placeOrder() {
paymentService.processPayment();
}
}
- Injects an EJB directly without JNDI lookups.
- The container manages transactions, security, and lifecycle.
2. @Resource
– Resource Injection (JNDI)
import jakarta.annotation.Resource;
import javax.sql.DataSource;
public class UserRepository {
@Resource(lookup = "java:jboss/datasources/MyDS")
private DataSource dataSource;
public void saveUser(String username) {
// use dataSource for JDBC operations
}
}
- Used for external resources like
DataSource
, JMS queues, mail sessions. - More low-level than CDI, directly tied to JNDI names.
3. @Inject
– CDI (Contexts and Dependency Injection)
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class NotificationService {
public void sendNotification(String msg) {
System.out.println("Notification: " + msg);
}
}
public class UserService {
@Inject
private NotificationService notificationService;
public void registerUser(String username) {
notificationService.sendNotification("User " + username + " registered");
}
}
- The modern, recommended way of injecting dependencies in Jakarta EE.
- Type-safe, extensible, and integrates with scopes (
@RequestScoped
,@SessionScoped
, etc.).
How Reflection Powers These Annotations
- The container uses reflection to scan classes for these annotations.
- At deployment, it wires fields, constructors, or methods marked with them.
- Proxies are created for transactional, scoped, or remote objects.
- Developers see only plain annotations, but the container dynamically injects dependencies.
📌 What's New in Java Versions?
- Java 5 – Introduced annotations, EJB 3.0 standardized
@EJB
. - Java 6/7 – Widespread adoption of
@Resource
. - Java EE 6 / Java 7 – CDI and
@Inject
introduced, shifting preference from@EJB
. - Jakarta EE 9 – Package renaming from
javax.*
tojakarta.*
. - Java 17/21 – No major new annotation changes, but CDI is now the standard for DI.
Pitfalls and Best Practices
Pitfalls
- Using
@EJB
where CDI@Inject
is better, leading to tight coupling. - Hardcoding JNDI names in
@Resource
. - Not managing CDI scopes properly, causing memory leaks or unexpected behavior.
Best Practices
- Prefer
@Inject
over@EJB
for new code. - Use
@Resource
sparingly and document JNDI lookups. - Keep beans properly scoped (
@ApplicationScoped
,@RequestScoped
). - Combine CDI with MicroProfile Config for portable applications.
Summary + Key Takeaways
@EJB
,@Resource
, and@Inject
are fundamental for dependency injection in Jakarta EE.- Use
@EJB
for legacy EJBs,@Resource
for JNDI-managed resources, and@Inject
for CDI beans. - Reflection and proxies allow the container to wire dependencies automatically.
- Modern Jakarta EE encourages CDI (
@Inject
) as the go-to annotation.
FAQ
-
What’s the difference between
@EJB
and@Inject
?@EJB
is for Enterprise Java Beans, while@Inject
is part of CDI and more flexible. -
Can I replace all
@EJB
with@Inject
?
Usually yes, but if you need EJB-specific services like transactions, stick with@EJB
. -
Is
@Resource
still relevant?
Yes, for injecting infrastructure resources like data sources and JMS. -
Does
@Inject
require a no-arg constructor?
No, CDI can inject through constructors and fields. -
How does reflection handle injection?
The container inspects annotations at runtime and assigns proxies or instances. -
Can I mix CDI and EJBs?
Yes, CDI beans can inject EJBs and vice versa. -
What happens if multiple beans match
@Inject
?
The container throws an ambiguity error unless you use qualifiers (@Qualifier
). -
Does MicroProfile extend these annotations?
MicroProfile builds on CDI and adds config, fault tolerance, health checks, etc. -
Is
@Resource
portable across servers?
Mostly, but JNDI names may differ between application servers. -
Which annotation should I prefer for new applications?
Use@Inject
with CDI for clean, modern, and portable dependency injection.