【问题标题】:dagger 2 circular dependency匕首2循环依赖
【发布时间】:2015-05-08 10:39:57
【问题描述】:

在我正在处理的一个项目中,我有 2 个高度相互依赖的类:

@Singleton
class WorkExecutor {
    @Inject Provider<ExecutionServices> services;
    ...
    public void execute(Work w){
        w.execute(services.get());
        ...
    }
    ...
}

class ExecutionServicesImpl implements ExecutionServices {
    @Inject WorkExecutor executor;
    ...
}

这个想法是,当执行一个工作时,该工作可以访问多个服务 - 其中一个是执行器本身,因此一个工作将能够执行子工作。

正如我们所看到的,这里有一个循环依赖,但我发现它很难打破。

主要问题是 WorkExecutor 在构建图时实际上并不需要 ExecutionServices 对象的实例,而只需要稍后使用的提供程序。遗憾的是,Dagger 不知道 WorkExecutor 不会从类的构造函数中调用 ExecutionServices 提供程序,因此它猜测 ExecutionServices 依赖于 WorkExecutor,反之亦然。

我发现的一个可能的解决方案是按以下方式定义模块和组件:

interface DelayedProvider<T> extends Provider<T>{}

@Module
class AppModule {
    Provider<ExecutionServices> delayedProvider = null;

    @Provides DelayedProvider<ExecutionServices> provideDelayed() {
        return () -> delayedProvider.get();
    }

    @Provides @Named("late-binding-conf") Void latebindingConf(Provider<ExecutionServices> eager){
        this.delayedProvider = eager;
        return null; //notice we returning Void and not void
    }
}

@Component(modules=AppModule.class)
interface AppComponent {
    App app();
    @Named("late-binding-conf") Void configureLateBinding();
}

然后我将原来的类修改为:

@Singleton
class WorkExecutor {
    @Inject DelayedProvider<ExecutionServices> services;
    ...
    public void execute(Work w){
        w.execute(services.get());
        ...
    }
    ...
}

class ExecutionServicesImpl implements ExecutionServices {
    @Inject WorkExecutor executor;
    ...
}

然后为了创建我的应用,我必须这样做:

AppComponent acomp = DaggerAppComponent.create();
App = acomp.app();
acomp.configureLateBinding();

但我不确定这是正确的做法 - 有没有更好的方法?

【问题讨论】:

  • 我遇到了类似的问题。你有没有找到更好的解决方案?
  • @KennethJ,很遗憾没有..
  • 为什么ExecutionServicesImpl 需要WorkExecutor?你说这是为了让一个作品可以执行子作品。难道你不能让WorkExecutor 将自己传递给Workexecute 方法而不是ExecutionServicesImpl 的成员吗?
  • Lazy 有什么办法可以解决这个问题吗?
  • @GregBacchus,我不知道..

标签: java dagger-2


【解决方案1】:

我不怀疑 OP 会喜欢这样,因为您想要一种方法来制造“错误”、“正确”工作。这不可能。每当遇到循环依赖时,“正确”的解决方案是重构以移除该依赖。

在您的情况下,WorkExecutor 是一个单例,因此它可能需要保持原样。然后应该修改 ExecutionServicesImpl 以删除对 WorkExecutor 的依赖。在不知道代码细节的情况下,不能说太多。然而,让 ExecutionService 独立于它的“worker”会减少耦合,从长远来看可能是一件非常好的事情。

【讨论】:

    【解决方案2】:

    为什么 ExecutionServicesImpl 依赖于 WorkExecutor?

    显示更多核心,ExecutionServices听起来也像Singleton,为什么要相互依赖才能正常工作?

    WorkExecutor 听起来像是你可以传递给ExecutionService 的东西,因为WorkExecutor 将被注入到其他地方,也许是使用Service 的那个。

    我不知道,显示更多代码可能就是答案,它看起来很复杂。

    【讨论】:

      【解决方案3】:

      我在 Swing 项目中遇到了一种情况,其中第三方对象依赖于 JFrame,而 JFrame 依赖于第三方对象来生成其内容窗格。我想出了这个解决方案,但最终决定在构建对象图后在我的 main 方法中关闭循环。基本上,我创建了两个名为 JFrame 的提供者,第二个取决于第一个并返回相同的实例:

      @Provides
      @Singleton
      @Named("DO_NOT_INJECT")
      JFrame mainWindowIncomplete() {
          return new JFrame(); // after setting it up
      }
      
      @Provides
      @Singleton
      CControl dockControl(@Named("DO_NOT_INJECT") JFrame mainWindow) {
          return new CControl(mainWindow);
      }
      
      @Provides
      @Singleton
      @Named("MAIN_WINDOW")
      JFrame mainWindow(@Named("DO_NOT_INJECT") JFrame mainWindow, CControl dockControl) {
          mainWindow.add(dockControl.getContentArea());
          return mainWindow;
      }
      

      为此,必须至少使用第二个提供程序一次。您可以通过将 package-private 限定符添加到第一个限定符来确保这一点,如 this answer 中所述。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多