How to resolve circular dependency when using Dagger2?
I have two classes, Foo<T>
and Bar
, which depend on each other, as well as various other classes. I use Dagger-2 for dependency injection, but if I naively add a circular dependency, Dagger removes the stack overflow at runtime. What's a good way to refactor the classes to fix this, while still using Dagger to inject all other dependencies and with minimal duplication and changes to existing calls?
source to share
After too many thoughts and conversations with colleagues, we ended up with the following:
class Foo<T> extends FooWithoutDep<T> {
@Inject Foo(Bar bar, OtherDep1 dep1, OtherDep2 dep2) {
super(dep1, dep2);
setBarDep(bar);
}
}
class FooWithoutDep<T> {
//Field declarations elided
@Inject FooWithoutDep(OtherDep1 dep1, OtherDep2 dep2) {
//Normal constructor stuff
}
void setBarDep(Bar bar) { this.bar = bar; }
//The rest of the actual logic
}
class Bar {
//Field declarations elided
@Inject Bar(FooWithoutDep<Thing> foo, OtherDep3 dep3) {
this.foo = foo;
this.foo.setBarDep(this);
this.dep3 = dep3;
}
//Code that uses Foo and the other dependencies
}
Explaining this, we moved the actual logic of Foo into the parent class (FooWithoutDep), which took the circular dependency as a custom field, not a constructor parameter. Back then, the original class just contained a constructor that took the circular dependency and called the setter. Another class, Bar, depended on the parent (FooWithoutDep) and explicitly called setter by passing itself ( this
). This allows all existing class references to remain unchanged while still using the dagger to inject all dependencies.
This seemed pretty confusing to us to write here.
source to share
This is how I solved it, without parent classes.
Class 1: Engine. (in component interface) @Provides public Engine myEngine (Context context) {return new Engine (context); }
Class 2: Spare parts. The engine also needs an instance of Parts, but creation is delayed.
@Inject
public Parts(Context context, Engine engine) {
this.context = context;
this.engine= engine;
engine.setParts(this);
}
A circular dependency can be achieved, but one class must be initiated first before the other.
Again, refactor your code if possible to avoid circular DI.
source to share