我在桌面应用程序中有这种代码
这只是一个包含按钮等的JPanel。
class ApplicationPanel {
private Listener listener;
public ApplicationPanel(){
this.listener = new Listener(this);
}
}
这会将事件添加到上面JPanel中的控件。
class Listener {
private ApplicationPanel panel;
public Listener(ApplicationPanel panel){
this.panel = panel;
}
}
来电代码就像这样
public void main(String[] args){
ApplicationPanel panel = new ApplicationPanel();
}
如果我尝试应用依赖注入 和工厂(后来改为Guice)
class ApplicationPanel {
private Listener listener;
public ApplicationPanel(){
this(new Listener());
}
public ApplicationPanel(Listener listener){
this.listener = listener;
}
}
class Listener {
private ApplicationPanel panel;
public Listener(){
this(new ApplicationPanel());
}
public Listener(ApplicationPanel panel){
this.panel = panel;
}
}
来电代码就是这样的 如您所见,此代码中存在循环依赖关系 什么是解决这个问题的最佳方法?
public void main(String[] args){
Listener listener = new Listener(panel);
ApplicationPanel panel = new ApplicationPanel(listener);
}
答案 0 :(得分:3)
查看有关该主题的this blog post;鉴于您在guice模块中提供的绑定,guice足够聪明,可以检测循环引用,然后使用临时代理,以便解决注入问题。
答案 1 :(得分:2)
一般解决方案是:使用依赖注入时,始终使用默认构造函数和setter。
这样,您可以创建所有对象(此阶段没有依赖项),立即调用所有setter,然后开始使用对象。
答案 2 :(得分:2)
问题是你无法使用假ApplicationPanel
对Listener
进行单元测试,因为Listener
接受ApplicationPanel
(如果您使用Listener
的默认构造函数监听器将有一个引用真实ApplicationPanel
的字段。你可以使用带有模拟框架的模拟Listener
,但在这种情况下,我认为循环依赖可能表示代码气味。
这种循环依赖的真正问题是Listener
在ApplicationPanel
完成构建之前收到对ApplicationPanel
的引用。这可能会导致线程安全问题。即使代码路径不是多线程的,Listener
也可能会在ApplicationPanel
初始化侦听器列表之前调用导致事件发送的ApplicationPanel
!
而是在收到事件时将数据传递给侦听器:
public interface Listener {
void onApplicationChanged(ApplicationPanel panel){
}
}
将侦听器添加到发送事件的对象的更传统的方法是进行方法调用:
public class ApplicationPanel {
private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
public void addListener(Listener listener) {
listeners.add(listener);
}
}
如果您希望发送事件的类使用构造函数指定侦听器,并且您想要使用Guice,请查看Multibinder。 ApplicationPanel
看起来像这样:
public class ApplicationPanel {
private Set<Listener> listeners;
@Inject
public ApplicationPanel(Set<Listener> listeners) {
listeners = new HashSet<Listener>(listeners);
}
}