为啥无状态 bean 被视为伪作用域并且不能具有循环依赖关系?

Posted

技术标签:

【中文标题】为啥无状态 bean 被视为伪作用域并且不能具有循环依赖关系?【英文标题】:Why stateless beans are treated as pseudo-scoped and cannot have circular dependencies?为什么无状态 bean 被视为伪作用域并且不能具有循环依赖关系? 【发布时间】:2014-12-15 13:13:29 【问题描述】:

使用 Wildfly 8.1 我有几个 bean,我尝试将几个 EJB 相互注入。假设我有 3 个豆子:

@Stateless 
public class A
  @Inject
  private B b;


@Stateless 
public class B
  @Inject
  private C c;


@Stateless 
public class C
  @Inject
  private A a;

显然,我有循环依赖。根据规范:

容器需要支持 bean 中的循环 至少有一个 bean 参与其中的依赖关系图 循环依赖链具有正常范围

在容器中运行上述代码会导致以下形式的错误:

org.jboss.weld.exceptions.DeploymentException: WELD-001443: Pseudo scoped bean 有循环依赖。依赖路径:

-会话 bean [class A with 限定符 [@Default @Any];本地接口是 [一种] BackedAnnotatedField] @Inject private B,

[..]

我的问题是:@Stateless bean 的范围是什么?默认情况下是@Dependent吗?最重要的是如何启用无状态会话 bean 之间的循环依赖关系?

对不起,如果问题太琐碎。我将感谢任何可以解释所呈现行为的好的进一步阅读资源。提前致谢。

更新 好的。我找到了解决方法。我使用了@EJB 注释而不是@Inject,但这并不能解释@Inject 的奇怪行为。这个问题仍然悬而未决,但正如 Mika 所说,这可能是 CDI 规范和 Weld RI 中未解决的问题。

【问题讨论】:

我没有答案,但这不是一个小问题。 CDI EG 成员之间就此进行了讨论。看看 CDI spec jira,应该有关于这个话题的问题。 @MikeBraun 您能否提供指向 jira 问题的链接?我已经搜索过了,但我不确定我找对了。 它是 CDI-414,见 issues.jboss.org/browse/CDI-414 谢谢。正如它在描述中所写的那样,当前的解决方法与我在更新中所写的相同。所以现在我会把它作为一个答案。 CDI-414 处理自注入,而不是循环注入。 【参考方案1】:

这是 wildfly/jboss CDI 实现中的一个错误。问题描述https://issues.jboss.org/browse/CDI-414 中提供的当前解决方法(由@MikeBraun 建议)是使用@EJB 注释而不是@Inject。

【讨论】:

【参考方案2】:

@Stateless 没有作用域,并且与任何作用域都没有关联。您的 bean 以 @Dependent 结尾,因为您没有在 bean 上注释任何其他范围。

你需要给他们一个正常的范围 - @RequestScoped@ApplicationScoped,但我不确定它们是否适合你的情况。

【讨论】:

您的解决方案只有在 Web 层内进行管理时才有效。因为它是 EJB bean,所以它是 CDI 实现中的一个错误。 我不同意这一点。确定无状态范围在这里毫无意义,我认为它甚至行不通。只有有状态的 bean 可以有范围。事实上,对于“@Stateless”这一现象,CDI 并没有真正令人信服的答案。无状态 bean 更像是一个监听消息的端点,而不是一个普通的 bean。这就是为什么自注入和循环依赖都不是问题的原因。无状态代理更像是一个 URL。当一个方法调用发生时,它被发送到该类型的任何 bean(一个新实例,一个来自池的实例,总是相同的,......)。所以这里的请求范围是完全错误的。【参考方案3】:

这并不能回答整个问题,但是当我搜索循环依赖异常时,它是谷歌上的第一次点击。希望这将帮助其他程序员在这里找到更快的答案是我的解决方案。

导致循环依赖异常的代码:

class A
    @Inject B b;
    public void foo()
        b.bar();
    
    public void quux()
        //some code
    

class B
    @Inject A a;
    public void bar()
        //some code
    
    public void baz()
        a.quux();
    

解决问题的方法是使用javax.enterprise.inject.Instance

class A
    @Inject B b;
    public void foo()
        b.bar();
    
    public void quux()
        //some code
    

class B
    @Inject Instance<A> a;
    public void bar()
        //some code
    
    public void baz()
        a.get().quux();
    

【讨论】:

以上是关于为啥无状态 bean 被视为伪作用域并且不能具有循环依赖关系?的主要内容,如果未能解决你的问题,请参考以下文章

为啥具有相同名称但不同签名的多个继承函数不会被视为重载函数?

无状态会话 Bean 与单例会话 Bean

有状态bean和无状态的bean

为啥这些 C++ STL 无序集不被视为相等?

为啥基于表单的身份验证不被视为 RESTful?

为啥 CORS 标头不被视为 XHR 调用的一部分?