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?

+7


source to share


3 answers


The easiest way out is to use it Lazy<T>

on one side.



Lazy<Foo> foo;

@Inject
Bar(Lazy<Foo> foo) {
    this.foo = foo;
}

// use foo.get(); when needed

      

+17


source


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.

+2


source


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.

0


source







All Articles