One thing I forgot to mention - if I annotate the Singleton EJBs with @ApplicationScoped then some of them (not all of them) throw errors saying that they are Normal Scoped beans that can’t be proxied. The classes aren’t serializable, but since they’re singletons I don’t think they have to be - and in any case I have other non-serializable classes that don’t throw the same error. My best guess at the difference is that the beans that DO seem to accept the ApplicationScoped annotation are local NO-interface beans, and the ones that throw the error have an explicitly declared local interface.
I’ve seen several examples of code that use both @Singleton and @ApplicationScoped annotations on the same bean - I’m not sure what difference having both annotations makes, since a singleton EJB is already application scoped in its nature. Can anyone explain?
When you use @Inject to obtain a reference to a bean, in most cases you get a proxy object. I'd guess that the proxy object is sub-classing your bean and overriding the methods. The EJB-bits (EJBObject & stub) *might* be doing something similar. So you're seeing you constructor called because these subclasses are calling super(). When you leave off @ApplicationScoped your are using the dependent pseudo-scope which doesn't use CDI proxies.
If you add the @ApplicationScoped annotation to your EJB or producers (if you have them) then you are asking for a CDI proxy to be injected at your @Inject injection points. The @ApplicationScoped proxies maintain reference to the beans in a shared application context. However when your CDI managed bean is actually an EJB then the thing in the application scope is really an EJB stub. This is a huge deal when injection something like a stateful session bean in a request scope for a servlet but it ends up being kind of silly when you've got your case, a stub to a singleton ejb stored in a shared application scope.
Adding @ApplicationScoped does impact some other things like the life-cycle of your producers and such.
You might as well leave off the @ApplicationScoped annotation from the singleton bean. Be careful about adding @ApplicationScoped and not @Singleton because then you wouldn't have an EJB and would lose container managed transactions and concurrency.
As for the error you only see when you add @ApplicationScoped to your bean. Look in the CDI spec for the rules about unproxyable beans. You have to make sure you don't have "classes which don't have a non-private constructor with no parameters" and "classes which are declared final or have final methods". But honestly sometimes I've seen the error go away when I clean and build my project or restart my server. Comment out the content of the class until you get the error to go away.
Using @Inject is better than using @EJB on the injection points. Whether you need a producer method or not depends on the complexity of your initialization.
Your reply explains a lot of things I hadn't quite got. I didn't realise that adding ApplicationScoped would be the trigger which would cause a bean to be proxied. If Weld is creating a subclassed proxy, as you say, then it makes sense that I would get a second instantiation when Weld tried to inject the EJB. Presumably the app container creates the
realEJB singleton at sone point, then when I try to inject it Weld is creating a subclassed instance in order to make the proxy. This is an odd situation though, because I still have multiple instances of the singleton class - if the constructor allocated any resources, for instance, they would be allocated multiple times. There should at least be a warning about this somewhere in the docs.
A simple guide on how to instantiate and inject singleton EJBs with Weld would be very useful. The Weld reference seems to always use @applicationScoped in conjunction with @singleton, but doesn't jeally explain why, or what effect it has. There's also the question of the mysterious javax.inject.singleton annotation and what that's supposed to be for...
Well, I did a bit more investigation. Firstly, Glassfish itself seems to make 2 instances of the singleton as it starts up the app:
• the first is created by a method called AbstractSingletonContainer.initializeHome which calls BaseContainer.instantiateOptionalEJBLocalBusinessObjectImpl
• the second instance is created by SingletonLifeCycleManager.initializeSingleton which xauses AbstractSingletonContainer.createSingletonEJB to be called.
So - at this point 2 instances have been created, but Weld hasn't done anything. After this the behaviour is different depending whether I annotated with ApplicationScoped or not. when Weld has to inject the Singleton then :
• if I DID annotate the bean as ApplicationScoped then 2 more instances are created by Weld : the first by weld.util.Proxies.createProxy and the second by weld.bean.SessionBean.create
• if I DONT annotate with applicationScoped then only 1 instance is created by weld - by SessionBean.create
Should I just try to relax about all this and just Trust The Magic ?
My app does seem to be working - I guess it's just important to remember not to allocate resources in the constructor of Singleton beans.
Right, an easier thing to do beside dig through the app server/weld code is just to add a line in your constructor that does:
System.out.println("I just built a: " + this.getClass().getName());
Just use a @PostConstruct method in your bean to take care of all the initialization and you'll be okay.
God no - I didn't root through the glassfish and Weld code that quickly! I just logged stack traces every time my object was constructed.
Yes - postConstruct seems to be the place to do any actual work - either I missed it or this isn't very well explained in the EJB docs though.