What is a Bean?
Think of a bean like a plant in a greenhouse:
- 🌱 The greenhouse (container) takes care of watering, temperature, light
- 🏭 You don't manually water each plant - the system does it
- 📦 Beans are objects that the container manages for you
- ♻️ Container handles their lifecycle, dependencies, and resources
A Bean is simply a Java object that is managed by a container (CDI container, EJB container, etc.). The container creates it, injects dependencies, manages its lifecycle, and destroys it.
Key Point: You don't create beans with 'new' - the container does it for you!
Regular Object vs Bean
// Regular Object - You manage everything
public class RegularClass {
private Database db = new Database(); // Manual creation
public RegularClass() {
// You handle initialization
}
public void cleanup() {
db.close(); // You handle cleanup
}
}
RegularClass obj = new RegularClass(); // You create it
obj.cleanup(); // You clean it up
// CDI Bean - Container manages everything
@ApplicationScoped // This makes it a bean!
public class ManagedBean {
@Inject // Container injects dependencies
private Database db;
@PostConstruct // Container calls after creation
public void init() {
// Automatic initialization
}
@PreDestroy // Container calls before destruction
public void cleanup() {
// Automatic cleanup
}
}
// You just inject it - container creates/manages it
@Inject
private ManagedBean bean; // Container provides it
Types of Beans in Jakarta EE
1. CDI Beans (Managed Beans)
Most common type - any POJO with a scope annotation
@RequestScoped // Scope makes it a CDI bean
public class UserService {
@Inject
private UserRepository repository;
public User findUser(Long id) {
return repository.find(id);
}
}
Characteristics:
- ✅ Managed by CDI container
- ✅ Supports @Inject for dependency injection
- ✅ Lifecycle callbacks (@PostConstruct, @PreDestroy)
- ✅ Scopes (@RequestScoped, @SessionScoped, @ApplicationScoped, etc.)
- ✅ Events and interceptors
2. Enterprise JavaBeans (EJB)
Server-side components with extra features
@Stateless // EJB annotation
public class PaymentService {
@Inject
private PaymentGateway gateway;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void processPayment(Order order) {
gateway.charge(order.getTotal());
}
}
Characteristics:
- ✅ Everything CDI beans have
- ✅ Plus: Transaction management (@TransactionAttribute)
- ✅ Plus: Security (@RolesAllowed)
- ✅ Plus: Asynchronous methods (@Asynchronous)
- ✅ Plus: Scheduled tasks (@Schedule)
- ✅ Plus: Remote access (can be called from other JVMs)
3. JPA Entities
Represent database tables
@Entity // JPA annotation
@Table(name = "users")
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String email;
// Getters/setters
}
Characteristics:
- ✅ Managed by JPA (persistence provider like Hibernate)
- ✅ Maps to database tables
- ✅ Automatic CRUD operations
- ✅ Relationships (@OneToMany, @ManyToOne, etc.)
- ❌ NOT CDI beans (unless you add a scope annotation)
4. JSF Managed Beans
Backing beans for JSF views (older style)
@Named // Makes it accessible from JSF views
@ViewScoped
public class UserController {
private String username;
private String password;
@Inject
private AuthService authService;
public String login() {
if (authService.authenticate(username, password)) {
return "dashboard?faces-redirect=true";
}
return null;
}
// Getters/setters for view binding
}
| Bean Type | Annotation | Container | Use Case |
|---|---|---|---|
| CDI Bean | @RequestScoped, @ApplicationScoped | CDI | General business logic |
| EJB | @Stateless, @Stateful | EJB + CDI | Transactions, security, timers |
| JPA Entity | @Entity | JPA | Database mapping |
| JSF Bean | @Named + scope | CDI | JSF view backing beans |
Bean Lifecycle
CDI Bean Lifecycle
1. Container scans for beans (at startup)
↓
2. User requests bean (via @Inject or direct access)
↓
3. Container creates instance (calls constructor)
↓
4. Container injects dependencies (@Inject fields/setters)
↓
5. Container calls @PostConstruct method
↓
6. Bean is ready to use
↓
... bean is used by application ...
↓
7. Scope ends (request ends, session expires, app shuts down)
↓
8. Container calls @PreDestroy method
↓
9. Bean is destroyed and garbage collected
Complete Lifecycle Example
@RequestScoped
public class LifecycleDemo {
// 1. Constructor called
public LifecycleDemo() {
System.out.println("1. Constructor called");
}
// 2. Dependency injected after construction
@Inject
private Database database;
// 3. Called after injection is complete
@PostConstruct
public void init() {
System.out.println("2. @PostConstruct - database is now available");
// Safe to use injected dependencies here
database.connect();
}
// 4. Business methods can now be called
public void doWork() {
System.out.println("3. Doing work with database");
database.query("SELECT * FROM users");
}
// 5. Called before bean is destroyed
@PreDestroy
public void cleanup() {
System.out.println("4. @PreDestroy - cleaning up");
database.disconnect();
}
}
Output during a request:
1. Constructor called
2. @PostConstruct - database is now available
3. Doing work with database
4. @PreDestroy - cleaning up
How to Make a Class a Bean
Option 1: Add a Scope Annotation (Most Common)
@RequestScoped // Now it's a CDI bean!
public class MyService {
// Bean code
}
Option 2: Add @Named (For JSF Access)
@Named // Accessible from JSF with #{myController}
@RequestScoped
public class MyController {
// Bean code
}
Option 3: Just Use It (Implicit Bean)
// No annotations needed if it has @Inject
public class SimpleService {
@Inject
private Database db; // Makes it a @Dependent bean
public void doSomething() { }
}
What Makes a Class NOT a Bean?
// ❌ Regular POJO - not a bean
public class RegularClass {
// No scope, no @Inject, no EJB annotation
public void doSomething() { }
}
// Created manually
RegularClass obj = new RegularClass(); // Not managed
// ❌ Entity alone - not a CDI bean
@Entity
public class User {
// JPA entity, but not a CDI bean unless you add a scope
}
Bean Discovery Modes
Configuration in beans.xml:
1. bean-discovery-mode="all" (Default in older versions)
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="4.0"
bean-discovery-mode="all">
<!-- All classes become beans -->
</beans>
All classes in the archive are CDI beans (even without annotations)
2. bean-discovery-mode="annotated" (Recommended)
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="4.0"
bean-discovery-mode="annotated">
<!-- Only annotated classes become beans -->
</beans>
Only classes with bean-defining annotations become beans (more performant)
3. bean-discovery-mode="none"
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="4.0"
bean-discovery-mode="none">
<!-- No automatic bean discovery -->
</beans>
CDI disabled for this archive (unless explicitly configured)
Proxies - How CDI Works Behind the Scenes
CDI doesn't inject actual objects - it injects proxies!
@RequestScoped
public class UserService {
public User getUser() {
return new User("Alice");
}
}
@ApplicationScoped
public class MyBean {
@Inject
private UserService userService; // This is a PROXY, not the real object!
}
Why proxies?
- ✅ @ApplicationScoped bean lives long, but @RequestScoped bean changes per request
- ✅ Proxy forwards calls to the correct instance based on current context
- ✅ Enables scope mismatches to work correctly
- ✅ Allows interception and AOP
Proxy Requirements
- ✅ Class must not be final
- ✅ Methods must not be final
- ✅ Must have a no-arg constructor (can be private)
- ❌ Abstract classes can't be proxied
Best Practices
✅ DO:
- Use CDI beans for business logic - Default choice
- Use EJBs only when needed - For transactions, security, timers
- Keep entities simple - No business logic in JPA entities
- Choose the right scope - @RequestScoped for most cases
- Use @PostConstruct for initialization - Dependencies are ready
- Make beans serializable if needed - Required for @SessionScoped
- Use bean-discovery-mode="annotated" - Better performance
❌ DON'T:
- Don't create beans with 'new' - Let the container manage them
- Don't make beans final - CDI needs to create proxies
- Don't put business logic in entities - Separate concerns
- Don't initialize in constructor - Injected fields aren't ready yet
- Don't store heavy objects in @SessionScoped - Memory issues
- Don't inject narrow scopes into wide scopes - Use Provider instead
Summary
- Bean = Java object managed by a container
- CDI Beans: General-purpose, @Inject support, lifecycle management
- EJBs: CDI beans + transactions, security, async, timers
- JPA Entities: Database mapping (not CDI beans unless scoped)
- JSF Beans: View backing beans with @Named
- Lifecycle: Constructor → Injection → @PostConstruct → Use → @PreDestroy → Destroy
- Scopes control: How long beans live and how many instances exist
- Proxies: CDI uses proxies to enable context awareness
- No 'new' keyword: Container creates and manages beans
- Benefits: Automatic lifecycle, dependency injection, loose coupling