Search code examples
javadependency-injectiondagger-2

How do I inject fields at runtime using Dagger 2?


I need to inject fields of an instance of one of my classes on-demand i.e., at runtime because I'm instantiating them on-the-fly.

I used to use Guice for this where I would call MembersInjector#injectMembers or Injector#injectMembers. How can I have something like this in Dagger 2?


Solution

  • Dagger 2 Components are the counterpart to Guice Injectors so the way to do this in Dagger 2 would be to specify the object whose field you want to inject at runtime as an injection site and request injection from the component.

    Let's say you have a CoffeeShop with fields you want to inject:

    class CoffeeShop {
    
        @Inject CoffeeMaker coffeeMaker;
    
        CoffeeShop() { 
            //we're not using constructor injection here 
            //although we probably should be :/
        }
    }
    

    You can specify CoffeeShop as an injection site inside a component and request injection from it:

    @Component(modules = { CoffeeModule.class })
    interface CoffeeComponent {
        void inject(CoffeeShop coffeeShop);
    }
    

    So inside another class you can do something like this:

    private CoffeeComponent coffeeComponent;
    
    void initComponent() {
        coffeeComponent = DaggerCoffeeComponent
                             .builder()
                             .coffeeModule(new CoffeeModule())
                             .build();
    }
    
    void makeCoffee() {  
        CoffeeShop coffeeShop = new CoffeeShop();
        coffeeComponent.inject(coffeeShop); //inject members of coffeeShop
        coffeeShop.makeCoffee();
    }
    

    Alternatively, you can define provision methods inside your Dagger 2 Components which will allow you to resolve instances of a class ad hoc.

    If you look at Jeff Bowman's example in the linked question, you can see there is a Component like this:

    @Component(modules = {/* ... */})
    public interface CoffeeShopComponent {
      CoffeeShop getCoffeeShop();
    
      void inject(CoffeeService serviceToInject); // to be discussed below
    }
    

    Say you then have a CoffeeService. You can now call getCoffeeShop() to obtain arbitrary instances of CoffeeShop:

    class CoffeeService extends SomeFrameworkService {
    
        private CoffeeComponent coffeeComponent;
    
        void initComponent() {
            coffeeComponent = DaggerCoffeeComponent
                                  .builder()
                                  .coffeeModule(new CoffeeModule());
                                  .build();
        }
    
        public CoffeeShop createCoffeeShop() {
            return coffeeComponent.getCoffeeShop(); //equivalent to Injector.getInstance();
        }
    }