【发布时间】:2025-11-27 10:40:01
【问题描述】:
这主要是关于了解使用@Inject 注入有状态的EJB (SFSB) 与使用@EJB 注入它时的差异。
主要区别之一应该是通过@Inject 注入时的上下文意识。所以我的假设是,如果我创建两个@RequestScoped bean 并在每个中注入一个SFSB 两次(一次使用@Inject,一次使用@EJB),则SFSB 通过@ 注入987654332@ 将是两个@RequestScoped bean 中的相同实例,而通过@EJB 注入的那些将是不同的实例。
这个假设似乎是错误的,但我不明白为什么。 CDI 不应该知道两个 bean 都是 @RequestScoped 并因此注入相同的 SFSB 吗?为什么不是这样,还是我的测试代码有些缺陷?
这是我的SFSB 及其界面:
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();
}
这是一个@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;
}
}
还有一个名为 MySecondRequestScoped 的 @RequestScoped bean 具有类似的实现。
当这些通过EL 从JSF xhtml 页面调用时(没什么特别的,只是一个<h:outputText value="#{myFirstRequestScoped.myStatefulByCDI}" /> 等等来触发它们的创建),这是控制台输出(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
看来:
-
创建了 4 个 SFSB 实例;我本来预计这将是只有 3。那些通过
@EJB注入的人不知道上下文,所以我认为如果为每个注入点创建它们就可以了。但是由于CDI应该知道上下文(@RequestScoped),我认为CDI会重新注入已经创建的SFSB。 -
@Inject和@EJB之间的唯一区别似乎在于,当通过CDI注入时,生命周期是自动管理的 - 为那些调用注解@PreDestroy的方法 (36b3bb10 和 456cec27)。通过@EJB(c03fcdee 和 9b3971c7)注入的这些后来仅被钝化,并且似乎不会在以后的任何时间被销毁。
后者似乎是使用@Inject而不是@EJB的一个很好的理由,但我不明白的是CDI的上下文意识的真正含义,无论范围如何,何时都会创建 SFSB 的新实例?
顺便说一句,使用 @SessionScoped bean 时的行为相同,即使第二个 bean 是在跟随到另一个页面的链接之后创建的(以确保 SFSB 通过@Inject 肯定已经存在)。此外,通过@EJB 注入的SFSB 实例在会话的生命周期内只创建一次,就像通过@Inject 注入的实例一样 - 所以这些似乎知道上下文,也,不知怎的……?当混合@SessionScoped 和@RequestScoped bean 时,@SessionScoped bean 得到SFSB 的另一个实例,而不是@RequestScoped bean,这很好 - 但似乎不是CDI 的某种特性,因为对于通过@Inject 注入的SFSB 实例以及通过@EJB 注入的实例both 都是如此。
编辑:观察到的行为的结论:
通过@Inject 和@EJB 注入SFSB 之间的唯一区别似乎是,在前一种情况下,SFSB 在离开作用域时会自动销毁,而在后一种情况下则不会。它是否正确?这让我觉得很奇怪,因为我预计 CDI 的行为会有所不同......
关于我缺少什么的任何提示,即对“CDI”中的“C”有误解?我希望这不是 WebSphere 的“专长”……
【问题讨论】:
-
@EJB似乎来自与 CDI 不同的技术时代。除其他外,它似乎使用 JNDI 进行查找。 -
当然,
@EJB比CDI存在的时间要长得多,CDI从版本 6 开始就是 Java EE 的一部分(请参阅 oracle.com/technetwork/java/javaee/tech/…)。但遗憾的是,对我来说,这并不能解释我上面解释的行为。
标签: dependency-injection java-ee-6 cdi ejb-3.1 websphere-8