In order for your SFSB to be scoped to the request, you need to give it the @RequestScoped
scope. Then you should see the same instance injected. Now since both of these are proxy'd, the easiest way to confirm is to set some value from one bean, and get the value from another bean.
Multiple instances of stateful EJB when injecting via CDI
-
24-09-2022 - |
質問
This is mostly about understanding the differences when injecting a stateful EJB
(SFSB
) with @Inject
compared to injecting it with @EJB
.
One of the main differences ought to be the contextual awareness when injecting via @Inject
. So my assumption was that if I create two @RequestScoped
beans and inject in each an SFSB
twice (once with @Inject
, once with @EJB
), the SFSB
injected via @Inject
would be the same instance in both @RequestScoped
beans, whereas the ones injected via @EJB
would be different instances.
This assumption seems to be wrong, but I do not understand why. Shouldn't CDI
be aware of the fact that both beans are @RequestScoped
and therefore inject the same SFSB
? Why is this not so, or is my test code somewhat flawed?
This is my SFSB
and its interface:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;
import package.MyStateful;
@Stateful
@Local(MyStateful.class)
public class MyStatefulImpl implements MyStateful {
@PostConstruct
private void postConstruct() {
System.out.println("SFSB postconstruct ref: " + this.toString());
}
@PreDestroy
private void preDestroy() {
System.out.println("SFSB predestroy ref: " + this.toString());
}
@PrePassivate
private void prePassivate() {
System.out.println("SFSB prepassivate ref: " + this.toString());
}
@Override
public String myToString() {
return toString();
}
}
public interface MyStateful {
String myToString();
}
And this is one @RequestScoped
bean:
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import package.MyStateful;
@Named
@RequestScoped
public class MyFirstRequestScoped {
@Inject
MyStateful myStatefulByCDI;
@EJB
MyStateful myStatefulByEJB;
public MyStateful getMyStatefulByCDI() {
System.out.println("first#myStatefulByCDI proxy ref: " + myStatefulByCDI.toString());
System.out.println("first#myStatefulByCDI stateful ref: " + myStatefulByCDI.myToString());
return myStatefulByCDI;
}
public MyStateful getMyStatefulByEJB() {
System.out.println("first#myStatefulByEJB proxy ref: " + myStatefulByEJB.toString());
System.out.println("first#myStatefulByEJB stateful ref: " + myStatefulByEJB.myToString());
return myStatefulByEJB;
}
}
There is another @RequestScoped
bean named MySecondRequestScoped
with an analoguous implementation.
When these are called from a JSF
xhtml page via EL
(nothing special, just a <h:outputText value="#{myFirstRequestScoped.myStatefulByCDI}" />
and so on to trigger their creation), this is the console output (WebSphere ApplicationServer 8.5.5.0):
[1/4/14 12:39:11:759 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl@c03fcdee
[1/4/14 12:39:11:761 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl@36b3bb10
[1/4/14 12:39:11:761 CET] 000000dc SystemOut O first#myStatefulByCDI proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7f98(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA11-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:762 CET] 000000dc SystemOut O first#myStatefulByCDI stateful ref: package.MyStatefulImpl@36b3bb10
[1/4/14 12:39:11:768 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl@9b3971c7
[1/4/14 12:39:11:768 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl@456cec27
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O second#myStatefulByCDI proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7fa1(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA18-0143-4000-E001-6A007F000001))
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O second#myStatefulByCDI stateful ref: package.MyStatefulImpl@456cec27
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O first#myStatefulByEJB proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7f9b(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA0E-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O first#myStatefulByEJB stateful ref: package.MyStatefulImpl@c03fcdee
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O second#myStatefulByEJB proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245@48da7fa1(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA18-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:770 CET] 000000dc SystemOut O second#myStatefulByEJB stateful ref: package.MyStatefulImpl@9b3971c7
[1/4/14 12:39:11:848 CET] 000000dc SystemOut O SFSB predestroy ref: package.MyStatefulImpl@36b3bb10
[1/4/14 12:39:11:849 CET] 000000dc SystemOut O SFSB predestroy ref: package.MyStatefulImpl@456cec27
[1/4/14 12:50:11:765 CET] 00000120 SystemOut O SFSB prepassivate ref: package.MyStatefulImpl@c03fcdee
[1/4/14 12:50:11:766 CET] 00000120 SystemOut O SFSB prepassivate ref: package.MyStatefulImpl@9b3971c7
So it seems that:
- 4 instances of the SFSB are created; I would have expected this to be just 3. Those injected via
@EJB
aren't aware of the context, so I would have thought it's ok if they're created for every injection point. But sinceCDI
should be aware of the context (@RequestScoped
), I thoughtCDI
would reinject theSFSB
already created. - The only difference between
@Inject
and@EJB
seems to be here that the life-cycle is automatically managed when injected viaCDI
- the method annotated@PreDestroy
is called for those (36b3bb10 and 456cec27). These injected via@EJB
(c03fcdee and 9b3971c7) are later only passivated and don't seem to be destroyed any time later.
The latter seems to be a good reason to use @Inject
instead of @EJB
, but what I don't understand is what is really meant by the contextual awareness of CDI
, when there's a new instance of the SFSB
created regardless of the scope?
By the way, this behaves the same when using @SessionScoped
beans, even if the second bean is created after following a link to another page (to make sure that the SFSB
injected via @Inject
definitely exists already). Moreover, the SFSB
instances injected via @EJB
are created just once for the lifetime of the session, just like the ones injected via @Inject
- so these seem to be aware of the context, too, somehow...? When mixing @SessionScoped
and @RequestScoped
beans, the @SessionScoped
bean gets another instance of the SFSB
injected than the @RequestScoped
bean, which is fine - but which seems not to be a feature of CDI somehow, since this is true for both those instances of the SFSB
injected via @Inject
as well as for those injected via @EJB
.
Edit: Conclusion of the observed behaviour:
The only difference between injecting an SFSB
via @Inject
and @EJB
seems to be that in the former case the SFSB
is automatically destroyed when the scope is left and in the latter case it's not. Is this correct? This would strike me as odd, since I expected CDI
to behave differently...
Any hints about what I'm missing, i.e. misunderstanding when it comes to the "C" in "CDI
"? I hope it's not some WebSphere "speciality"...
解決