Can I scope a CDI bean to be a singleton? Why am I seeing my @ApplicationScoped bean constructed twice?
sboscarine Feb 5, 2013 3:10 PMHello All,
Can I make a CDI bean be a singleton? I am having an issue w/ CDI and must be misunderstanding something very fundamental.
Here's what I'd like to do:
- Scope a bean so there's only 1 copy shared among all the applications to store application configuration data.
- Make it mutable to allow different classes to update it on rare occasion so we can:
- read the latest value without a reboot
- and not hit the source of the configuration value (a database) every time it's read.
You could say I want to use a CDI bean as a cache.
Here's what I did:
- Wrote a simple @ApplicationScoped bean to store my value
- Wrote 3 consumers that inject it using @Inject.
- Created an event listener that updates the value when I call a REST service.
- Added instrumentation to print when it is being constructed.
I noticed the following:
- 2 copies of the @ApplicationScoped bean were constructed (I had 3 consumers and was hoping only 1 would be constructed).
- When I update the value in one instances, my other instance doesn't see the modifications.
The main consumer
@WebListener public class WebSessionTimeoutFilter implements HttpSessionListener { private static final Logger logger = LoggerFactory.getLogger(WebSessionTimeoutFilter.class); @Override public void sessionCreated(HttpSessionEvent event) { final int timoutInMin = settings.getGUITimeoutInMinutes(); final int guiTimeoutInSeconds = timoutInMin * 60; event.getSession().setMaxInactiveInterval(guiTimeoutInSeconds);// in seconds logger.info("Session created. Timeout is " + timoutInMin + " min"); } @Override public void sessionDestroyed(HttpSessionEvent event) { } @Inject @WebappConfig private WebappSettings settings; }
Here's my bean:
@ApplicationScoped public class WebappSettings { public WebappSettings(){ System.out.println("\n" + this.getClass().getSimpleName() + " was constructed\n"); //This is called twice on startup. } @Min(1) @Max(180) //standard JSR303 BeanValidation private int GUITimeoutInMinutes; public int getGUITimeoutInMinutes() { return GUITimeoutInMinutes; } public String toString() { return "WebappSettings [GUITimeoutInMinutes=" + GUITimeoutInMinutes + "]"; } }
Here's the code to produce it and update it (same class):
//My update method. public void refreshValues(@SuppressWarnings("unused") @Observes ConfigurationUpdateEvent event) { logger.info("\n old value=" + currentSettings); MyUpdateLogic.updateMe(currentSettings, config.getFinalConfig()); //my update logic...works correctly, I can see the value updated as expected. logger.info("\n updated value=" + currentSettings); } @Inject @WebappConfig private WebappSettings currentSettings; //... //My producer method. @Produces @WebappConfig public WebappSettings produceWebappSettings() { WebappSettings settings = new WebappSettings(); MyUpdateLogic.updateMe(settings, config.getFinalConfig()); return settings; }
Why aren't all the consumers using the same instance as each other?
WebappSettings is @ApplicationsScoped. Why do I see it constructed twice on startup?
How can I make a bean a Singleton in CDI?