循环EJB依赖的缺点?

时间:2017-02-22 11:24:17

标签: java jboss dependencies ejb

假设我们有两个EJB ServiceAServiceBServiceA提供了ServiceB中所需的公共方法。与此同时,ServiceB中存在ServiceA

这种循环依赖有问题吗?

@Stateless
public class ServiceA implements IServiceA {

  @EJB(mappedName = IServiceB.JNDI_NAME_LOCAL)
  private IServiceB serviceB;

  // ...

  public void foo() {
    // ...
  }
}

@Stateless
public class ServiceB implements IServiceB {

  @EJB(mappedName = IServiceA.JNDI_NAME_LOCAL)
  private IServiceA serviceA;

  // ...

  private void bar() {
    serviceA.foo();
  }
}

我总是试图避免这种架构,但最近一位同事介绍了这种相互使用方式。我认为在服务中使用服务是错误的,在服务中,它使用第一个再次使用第二个服务的服务......你得到它。从技术上讲,这显然有效,但我对它并不完全满意,宁愿为ServiceC方法引入foo()

所以我想知道:

  1. 这种循环依赖可以吗?

  2. 如果没有,是否有技术原因不这样做?

  3. 如果技术上可行的话,是否有任何针对它的论据?

2 个答案:

答案 0 :(得分:2)

你的问题超出了EJB,它是关于类之间的耦合。

  

是否可以拥有这种循环依赖?

否,因为为了防止不良的可维护性上升成本,如果不需要,两个类应该避免它们之间的强耦合。

在这里你有一个很强的耦合,因为A知道B而B知道A.

  

如果没有,是否有技术原因不这样做?

循环依赖关系的解决方案可能会因某些DI容器而变得复杂,但事实上最严重的问题并不在这里。

具有双向依赖关系(A看到B和B看到A),无论我改变A或B:方法(返回的类型和参数)类层次结构等等,另一个类都可能受到影响。

例如,在您的代码中,B使用A.的foo()方法 假设A使用B.的<{1}}方法

如果我通过添加新参数来更改A的bar()方法,则A和B都会被修改,如果我通过添加新参数来更改B的foo()方法,那么A和B被修改。
这清楚地表明了类之间的责任定义问题。它鼓励在未来的变化中混合责任,它降低了代码的可读性和设计的一致性,因此它有利于副作用和回归,因为这些类中的任何一个都被修改。

虽然具有单向依赖性(A看到B但B看不到A),如果我改变A,B不受影响,因为B不知道A.
所以赞成单向依赖。

  

从技术上讲,这显然有效,但我对此并不满意,宁愿为foo()方法引入一个ServiceC。

我们没有很多关于执行逻辑的细节,但你想引入一个中间类以避免双向依赖的想法似乎相当不错,因为它阻止了强耦合

另一种处理问题的方法是,如果通过移动ServiceB类中的foo()方法来更改类的职责是有意义的。这样,如果serviceB仅用于调用foo()方法,它就不再需要依赖serviceA。

答案 1 :(得分:0)

除此之外,这不是一个好的设计。重点是,如果你不使用Sinlgeton bean,ejb字段中的循环依赖可能会导致相当大的问题。事实上,该规范引用了一句话:

  

容器必须确保只有一个线程可以执行a   任何时候无状态或有状态会话bean实例。的因此,   有状态和无状态会话bean不必编码为   折返即可。这条规则的一个含义是应用程序不能   对无状态或有状态会话bean实例进行环回调用。

如果您已经使用了很多时间并且没有发现任何问题或死锁,那是因为Application Server已经为您管理/解决了这个问题。例如,Weblogic具有此功能。因此,我建议先使用您正在使用的应用程序服务器进行检查。消息驱动Bean也是如此。

如果您使用的是单身,那么根本没有问题。再次引用规范:

  

特殊锁定语义适用于Singletons上的环回调用   容器管理的并发。

     

如果在已经在同一线程上持有写锁定的Singleton上发生了回调调用:

     
      
  • 如果回送调用的目标是Read方法,则Read lock必须   总是被立即授予,而不释放原始的Write   锁。
  •   
  • 如果回送调用的目标是Write方法,则调用   必须立即进行,而不释放原始的写锁定。
  •   
     

如果在包含Read锁定的Singleton上发生回调调用   相同的线程(但也不在同一个线程上持有Write锁):

     
      
  • 如果回送调用的目标是Read方法,则必须进行调用   立即进行,而不释放原始的Read锁。
  •   
  • 如果回送调用的目标是Write方法,则a   必须向调用者抛出javax.ejb.IllegalLoopbackException。
  •   

作为最后一点,我发现您正在使用mappedName,但所有应用程序服务器都不支持此功能,并且每个应用程序服务器都可能提供不同的名称。您应该使用lookup