Dagger - nested injections, is it necessary to call inject()?

蹲街弑〆低调 提交于 2019-12-04 11:59:05

You should rather use constructor injection (like for example the Thermosiphon does), and avoid field injection unless necessary. For example, let your GameManager have the Board as a constructor argument:

@Singleton
public class GameManager  {

  private final Board mBoard;

  @Inject
  public GameManager(final Board board){
      mBoard = board;
  }
}

Dagger will use this constructor to create an instance of the GameManager (hence the @Inject annotation), and notice it needs a Board instance. Using the ObjectGraph, it will create a Board first, and use that instance to create the GameManager. You can remove the @Provides GameManager method if you do it this way.

In your case, you have a @Provides Board method in your module. If you add an @Inject annotation to your Board constructor, you can remove this provides-method from your module:

public class Board {

  @Inject
  public Board() {
  }
}

If you don't want to use constructor injection, the problem is that you told Dagger that you want to create your GameManager instance yourself (because you have the @Provides GameManager method). If you remove this method, and let Dagger create it for you like above but without the Board parameter in the constructor, Dagger will also notice the @Inject Board field and inject that as well.


A final remark. Remove the library = true and complete = false statements! These are not necessary at all in this example. Only add them if you really know what you're doing. By not having them, Dagger will create compile-time errors to notify you that something is wrong. If you do include them, you're telling Dagger "Hey, I know what I'm doing, don't worry, it's all correct", when in fact it isn't.


Edit

A quote from the Dagger1 site:

If your class has @Inject-annotated fields but no @Inject-annotated constructor, Dagger will use a no-argument constructor if it exists. Classes that lack @Inject annotations cannot be constructed by Dagger.

I do not use this method very often, so I could be wrong. I think this means that you should remove the @Inject annotation from your constructor, like so:

@Singleton
public class GameManager  {

  @Inject Board mBoard;

  public GameManager(){ // Or remove the constructor entirely since it's empty
  }
}

Since there is an @Inject annotation on the Board field, Dagger will know to use the no-argument constructor.

I was struggling with the same issue as most of the dagger examples everywhere use a Module with Provides and I had a hard time finding a complete example that just does not use Provides.

I created this one. It uses field injection (not constructor injection) and works just fine through the hierarchy without requiring any call to inject. I am using Dagger 1.2.2.

Main.java

import javax.inject.*;
import dagger.*;
import dagger.ObjectGraph;

public class Main {

    public static void main(String[] args) {

        ObjectGraph objectGraph = ObjectGraph.create(new CarModule());
        Car car = objectGraph.get(Car.class);
        car.start();
    }
}

CarModule.Java

import dagger.Module;

@Module(injects = Car.class)
public class CarModule {

}

Car.Java

import javax.inject.*;

public class Car {
    @Inject public Engine engine;

    @Inject Car() {
        System.out.println("Car constructor");
    }
    public void start() {
        engine.start();
    }

}

Engine.Java

import javax.inject.*;
public class Engine {
    @Inject WaterPump waterPump;

    Engine() {
        System.out.println("Engine Constructor");
    }
    void start() {
        waterPump.run();
        System.out.println("starting engine.");
    }
}

WaterPump.Java

import javax.inject.*;
public class WaterPump {
    @Inject WaterPump() {
        System.out.println("WaterPump Constructor.");
    }

    public void run() {
        System.out.println("WaterPump running.");
    }
}

The output is:

Car constructor
Engine Constructor
WaterPump Constructor.
WaterPump running.
starting engine.

Without The CarModule that declares it injects Car.Class this does not work. You get:

Exception in thread "main" java.lang.IllegalArgumentException: No inject registered for members/Car. You must explicitly add it to the 'injects' option in one of your modules.

But notice that CarModule does not @Provides anything. It is dagger that automatically creates all the dependencies using the object graph.

Also note that you don't have to place an @Inject annotation on the default constructor if you have a @Inject field in the class. For Car class I used it on both the constructor and the field and in Engine class I used it just for the field and not for the constructor and it works fine as documented.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!