MyManual: Dagger
July 30, 2022
Learning notes about Dagger.

@Inject annotation

1. On constructor

Automatically obtains an instance of parameters and invokes the constructor.

2. On field

“If your class has @Inject-annotated fields but no @Inject-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the @Inject annotation to indicate that Dagger may create instances as well.” - Link

@Provides annotation

Inject annotation will call the injected constructor to initialize the object. @Provides annotation enables customizable method to “provide” injected object.

@Provides static Heater provideHeater() {
  return new ElectricHeater();
}

Whenever object Heater is requested, this provideHeader() will be called.

To create an alias of a type:

@Provides static Heater provideHeater(ElectricHeater heater) {
  return heater;
}

@Binds annotation

@Binds Heater bindHeater(ElectricHeater impl);

Can also create an alias of one type.

@Module annotation

All @Binds and @Provides are required to be put in a @Module annotated interface.

@Module
interface HeaterModule {
  @Binds Heater bindHeater(ElectricHeater impl);
}

@Component annotation

This annotation is used at Application level. By annotating an interface with @Component with defined modules, Dagger framework will create a concrete class named as “DaggerXXX” with builder().

@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
  CoffeeMaker maker();
}
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
    .dripCoffeeModule(new DripCoffeeModule())
    .build();

@Singleton annotation

Can be used to annotate injectable classes or providers.

@Reusable annotation

“That means that if you install a module with a @Reusable binding in a component, but only a subcomponent actually uses the binding, then only that subcomponent will cache the binding's object. If two subcomponents that do not share an ancestor each use the binding, each of them will cache its own object. If a component's ancestor has already cached the object, the subcomponent will reuse it.” – Link

Lazy wrapper

@Inject or @Provides annotated type can be wrapped in Lazy defers instantiation until the first call to get() method.

class GrindingCoffeeMaker {
  @Inject Lazy<Grinder> lazyGrinder;

  public void brew() {
    while (needsGrinding()) {
      // Grinder created once on first call to .get() and cached.
      lazyGrinder.get().grind();
    }
  }
}

Provider wrapper

A Provider invokes the binding logic for T each time .get() is called. If that binding logic is an @Inject constructor, a new instance will be created, but a @Provides method has no such guarantee. – Link

class BigCoffeeMaker {
  @Inject Provider<Filter> filterProvider;

  public void brew(int numberOfPots) {
  ...
    for (int p = 0; p < numberOfPots; p++) {
      maker.addFilter(filterProvider.get()); //new filter every time.
      maker.addCoffee(...);
      maker.percolate();
      ...
    }
  }
}

@Qualifier annotation

Can be used when object initialization cannot be determined only by its type. In Spring, @Name is used for “name” based wiring. In Dagger, you can annotate an annotation interface with @Qualifer to define your own wiring annotation.

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
  String value() default "";
}
class ExpensiveCoffeeMaker {
  @Inject @Named("water") Heater waterHeater;
  @Inject @Named("hot plate") Heater hotPlateHeater;
  ...
}
@Provides @Named("hot plate") static Heater provideHotPlateHeater() {
  return new ElectricHeater(70);
}

@Provides @Named("water") static Heater provideWaterHeater() {
  return new ElectricHeater(93);
}

@BindsOptionalOf annotation

@BindsOptionalOf abstract CoffeeCozy optionalCozy();

That means that @Inject constructors and members and @Provides methods can depend on an Optional object. If there is a binding for CoffeeCozy in the component, the Optional will be present; if there is no binding for CoffeeCozy, the Optional will be absent. – Link

@BindsInstance annotation

Marks a method on a component builder or a parameter on a component factory as binding an instance to some key within the component.

@Component(modules = AppModule.class)
interface AppComponent {
  App app();

  @Component.Builder
  interface Builder {
    @BindsInstance Builder userName(@UserName String userName);
    AppComponent build();
  }
}
public static void main(String[] args) {
  if (args.length > 1) { exit(1); }
  App app = DaggerAppComponent
      .builder()
      .userName(args[0])
      .build()
      .app();
  app.run();
}