假设我们有两个EJB ServiceA
和ServiceB
。 ServiceA
提供了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()
。
所以我想知道:
这种循环依赖可以吗?
如果没有,是否有技术原因不这样做?
如果技术上可行的话,是否有任何针对它的论据?
答案 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
。