質問

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 since CDI should be aware of the context (@RequestScoped), I thought CDI would reinject the SFSB already created.
  • The only difference between @Inject and @EJB seems to be here that the life-cycle is automatically managed when injected via CDI - 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"...

役に立ちましたか?

解決

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top