Singleton EJB vs CDI @ApplicationScoped
Replace Singleton EJBs with CDI @ApplicationScoped beans for simpler shared-state management.
@Singleton
@Startup
@ConcurrencyManagement(
ConcurrencyManagementType.CONTAINER)
public class ConfigCache {
private Map<String, String> cache;
@PostConstruct
public void load() {
cache = loadFromDatabase();
}
@Lock(LockType.READ)
public String get(String key) {
return cache.get(key);
}
@Lock(LockType.WRITE)
public void refresh() {
cache = loadFromDatabase();
}
}
@ApplicationScoped
public class ConfigCache {
private volatile Map<String, String> cache;
@PostConstruct
public void load() {
cache = loadFromDatabase();
}
public String get(String key) {
return cache.get(key);
}
public void refresh() {
cache = loadFromDatabase();
}
}
Less annotation noise
No @ConcurrencyManagement, @Lock, or @Startup — just a single @ApplicationScoped annotation.
Flexible concurrency
Use java.util.concurrent locks or volatile for exactly the thread-safety you need.
Easy testing
Plain CDI beans can be instantiated directly in tests without an EJB container.
Widely available since Jakarta EE 8 / Java 11
Singleton EJBs bundle concurrency management (@Lock, @ConcurrencyManagement) and eager initialisation (@Startup) into the EJB container. A CDI @ApplicationScoped bean achieves the same single-instance lifecycle with far less ceremony. When concurrency control is needed, standard java.util.concurrent utilities give you finer-grained control than the EJB lock annotations.