Dagger 2. Right way to do
Dependency Injections
by Anton Minashkin
Usual JAVA code
public class Payroll {
...
public long getWithholding(long payInDollars) {
...
return withholding;
}
public long getAfterTaxPay(Employee employee) {
long basePay = EmployeeDatabase.getInstance()
.getBasePay(employee);
long withholding = getWithholding(basePay);
return basePay - withholding;
}
}
Usual JAVA code
public class Payroll {
...
public long getWithholding(long payInDollars) {
...
return withholding;
}
public long getAfterTaxPay(Employee employee) {
long basePay = EmployeeDatabase.getInstance()
.getBasePay(employee);
long withholding = getWithholding(basePay);
return basePay - withholding;
}
}
Usual JAVA code
public class Payroll {
...
public long getWithholding(long payInDollars) {
...
return withholding;
}
public long getAfterTaxPay(Employee employee) {
long basePay = new EmployeeDatabase()
.getBasePay(employee);
long withholding = getWithholding(basePay);
return basePay - withholding;
}
}
What is DI?
In software engineering, dependency
injection is a software design pattern that
implements inversion of control for software
libraries.
What is DI?
Say “Hi!” to DI
public class Payroll {
...
EmployeeDatabase mEmployeeDatabase;
public Payroll(EmployeeDatabase employeeDatabase) {
mEmployeeDatabase = employeeDatabase;
}
public long getWithholding(long payInDollars) {
...
return withholding;
}
public long getAfterTaxPay(Employee employee) {
long basePay = mEmployeeDatabase.getBasePay(employee);
long withholding = getWithholding(basePay);
return basePay - withholding;
}
}
Say “Hi!” to DI
public class Payroll {
...
EmployeeDatabase mEmployeeDatabase;
public Payroll(EmployeeDatabase employeeDatabase) {
mEmployeeDatabase = employeeDatabase;
}
public long getWithholding(long payInDollars) {
...
return withholding;
}
public long getAfterTaxPay(Employee employee) {
long basePay = mEmployeeDatabase.getBasePay(employee);
long withholding = getWithholding(basePay);
return basePay - withholding;
}
}
...but!
Was:
new Payroll().getAfterTaxPay(employee);
Now:
new Payroll(EmployeeDatabase.getInstance())
.getAfterTaxPay(employee);
...but!
Was:
new Payroll().getAfterTaxPay(employee);
Now:
new Payroll(EmployeeDatabase.getInstance())
.getAfterTaxPay(employee);
Java! To the rescue!
JSR-330
JSR-330
public class Payroll {
...
@Inject
public Payroll(EmployeeDatabase employeeDatabase) {
mEmployeeDatabase = employeeDatabase;
}
...
}
OR
public class Payroll {
...
@Inject
EmployeeDatabase mEmployeeDatabase;
...
}
DI frameworks
● Google Guice
● Spring DI
● Java EE6 CDI
● Dagger
● etc.
DI frameworks
● Google Guice
● Spring DI
● Java EE6 CDI
● Dagger
● etc.
Dagger 2. History
Developed by Square
Adopted by Google (Dagger 2)
Dagger 2. Main features
● Android friendly
● JSR-330
● Compile-time DI validation
● Full stack code generation
● User mimic code
● Easy to debug & understand
Dagger 2. API
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
...
}
Dagger 2. API
class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
...
}
Dagger 2. API
@Module
class DripCoffeeModule {
@Provides Heater provideHeater() {
return new ElectricHeater();
}
@Provides Pump providePump(Thermosiphon pump) {
return pump;
}
}
Dagger 2. API
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
Dagger 2. API
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
...
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
Dagger 2. API
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
Dagger 2. Scope
@Provides @Singleton Heater provideHeater() {
return new ElectricHeater();
}
Dagger 2. Lazy
class GridingCoffeeMaker {
@Inject Lazy<Grinder> lazyGrinder;
public void brew() {
while (needsGrinding()) {
// Grinder created once on first call to .get() and cached.
lazyGrinder.get().grind();
}
}
}
Dagger 2. Provider Injection
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();
...
}
}
}
Dagger 2. Qualifiers
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
String value() default "";
}
Dagger 2. Qualifiers
class ExpensiveCoffeeMaker {
@Inject @Named("water") Heater waterHeater;
@Inject @Named("hot plate") Heater hotPlateHeater;
...
}
Dagger 2. Qualifiers
@Provides @Named("hot plate") Heater provideHotPlateHeater() {
return new ElectricHeater(70);
}
@Provides @Named("water") Heater provideWaterHeater() {
return new ElectricHeater(93);
}
Dagger 2. Compile-time validation
@Module
class DripCoffeeModule {
@Provides Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
...
[ERROR] COMPILATION ERROR :
[ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated
method.
Dagger 2. Generated code
@Override
public DataManager get() {
DataManager provided = module.provideDataManager(authApiProvider.get(), articleApiProvider.get(),
commentsApiProvider.get());
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
}
return provided;
}
public static Factory<DataManager> create(DataManagerModule module, Provider<AuthApi> authApiProvider,
Provider<ArticleApi> articleApiProvider, Provider<CommentApi> commentApiProvider) {
return new DataManagerModule_ProvideDataManagerFactory(module, authApiProvider, articleApiProvider,
commentApiProvider);
}
Dagger 2. Debugging
Here should be example of Guice stacktrace:
VERY-VERY-BAD-STACKTRACE
And here is Dagger stacktrace:
Mmmm… What a lovely stacktrace!
Dagger 2. What should I inject?
● Anything that has constructor parameters
● Anything that is out of local scope
● Infrastructure
● Anything that is shared between >1 objects
● Different obj-graphs for different flavors
Dagger 2. Where should I inject?
public class MyApp extends Application {
@Override
public void onCreate() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
...
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
inject(activity);
}
...
});
}
Dagger 2
Example
Dagger 2
Questions?
RTFM!
http://google.github.io/dagger/
https://youtu.be/oK_XtfXPkqw
anton.minashkin@outlook.com

Dagger 2. Right way to do Dependency Injection

  • 1.
    Dagger 2. Rightway to do Dependency Injections by Anton Minashkin
  • 2.
    Usual JAVA code publicclass Payroll { ... public long getWithholding(long payInDollars) { ... return withholding; } public long getAfterTaxPay(Employee employee) { long basePay = EmployeeDatabase.getInstance() .getBasePay(employee); long withholding = getWithholding(basePay); return basePay - withholding; } }
  • 3.
    Usual JAVA code publicclass Payroll { ... public long getWithholding(long payInDollars) { ... return withholding; } public long getAfterTaxPay(Employee employee) { long basePay = EmployeeDatabase.getInstance() .getBasePay(employee); long withholding = getWithholding(basePay); return basePay - withholding; } }
  • 4.
    Usual JAVA code publicclass Payroll { ... public long getWithholding(long payInDollars) { ... return withholding; } public long getAfterTaxPay(Employee employee) { long basePay = new EmployeeDatabase() .getBasePay(employee); long withholding = getWithholding(basePay); return basePay - withholding; } }
  • 5.
    What is DI? Insoftware engineering, dependency injection is a software design pattern that implements inversion of control for software libraries.
  • 6.
  • 7.
    Say “Hi!” toDI public class Payroll { ... EmployeeDatabase mEmployeeDatabase; public Payroll(EmployeeDatabase employeeDatabase) { mEmployeeDatabase = employeeDatabase; } public long getWithholding(long payInDollars) { ... return withholding; } public long getAfterTaxPay(Employee employee) { long basePay = mEmployeeDatabase.getBasePay(employee); long withholding = getWithholding(basePay); return basePay - withholding; } }
  • 8.
    Say “Hi!” toDI public class Payroll { ... EmployeeDatabase mEmployeeDatabase; public Payroll(EmployeeDatabase employeeDatabase) { mEmployeeDatabase = employeeDatabase; } public long getWithholding(long payInDollars) { ... return withholding; } public long getAfterTaxPay(Employee employee) { long basePay = mEmployeeDatabase.getBasePay(employee); long withholding = getWithholding(basePay); return basePay - withholding; } }
  • 9.
  • 10.
  • 11.
    Java! To therescue! JSR-330
  • 12.
    JSR-330 public class Payroll{ ... @Inject public Payroll(EmployeeDatabase employeeDatabase) { mEmployeeDatabase = employeeDatabase; } ... } OR public class Payroll { ... @Inject EmployeeDatabase mEmployeeDatabase; ... }
  • 13.
    DI frameworks ● GoogleGuice ● Spring DI ● Java EE6 CDI ● Dagger ● etc.
  • 14.
    DI frameworks ● GoogleGuice ● Spring DI ● Java EE6 CDI ● Dagger ● etc.
  • 15.
    Dagger 2. History Developedby Square Adopted by Google (Dagger 2)
  • 16.
    Dagger 2. Mainfeatures ● Android friendly ● JSR-330 ● Compile-time DI validation ● Full stack code generation ● User mimic code ● Easy to debug & understand
  • 17.
    Dagger 2. API classThermosiphon implements Pump { private final Heater heater; @Inject Thermosiphon(Heater heater) { this.heater = heater; } ... }
  • 18.
    Dagger 2. API classCoffeeMaker { @Inject Heater heater; @Inject Pump pump; ... }
  • 19.
    Dagger 2. API @Module classDripCoffeeModule { @Provides Heater provideHeater() { return new ElectricHeater(); } @Provides Pump providePump(Thermosiphon pump) { return pump; } }
  • 20.
    Dagger 2. API @Component(modules= DripCoffeeModule.class) interface CoffeeShop { CoffeeMaker maker(); }
  • 21.
    Dagger 2. API CoffeeShopcoffeeShop = DaggerCoffeeShop.builder() .dripCoffeeModule(new DripCoffeeModule()) .build(); ... CoffeeShop coffeeShop = DaggerCoffeeShop.create();
  • 22.
    Dagger 2. API publicclass CoffeeApp { public static void main(String[] args) { CoffeeShop coffeeShop = DaggerCoffeeShop.create(); coffeeShop.maker().brew(); } }
  • 23.
    Dagger 2. Scope @Provides@Singleton Heater provideHeater() { return new ElectricHeater(); }
  • 24.
    Dagger 2. Lazy classGridingCoffeeMaker { @Inject Lazy<Grinder> lazyGrinder; public void brew() { while (needsGrinding()) { // Grinder created once on first call to .get() and cached. lazyGrinder.get().grind(); } } }
  • 25.
    Dagger 2. ProviderInjection 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(); ... } } }
  • 26.
    Dagger 2. Qualifiers @Qualifier @Documented @Retention(RUNTIME) public@interface Named { String value() default ""; }
  • 27.
    Dagger 2. Qualifiers classExpensiveCoffeeMaker { @Inject @Named("water") Heater waterHeater; @Inject @Named("hot plate") Heater hotPlateHeater; ... }
  • 28.
    Dagger 2. Qualifiers @Provides@Named("hot plate") Heater provideHotPlateHeater() { return new ElectricHeater(70); } @Provides @Named("water") Heater provideWaterHeater() { return new ElectricHeater(93); }
  • 29.
    Dagger 2. Compile-timevalidation @Module class DripCoffeeModule { @Provides Heater provideHeater(Executor executor) { return new CpuHeater(executor); } } ... [ERROR] COMPILATION ERROR : [ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.
  • 30.
    Dagger 2. Generatedcode @Override public DataManager get() { DataManager provided = module.provideDataManager(authApiProvider.get(), articleApiProvider.get(), commentsApiProvider.get()); if (provided == null) { throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); } return provided; } public static Factory<DataManager> create(DataManagerModule module, Provider<AuthApi> authApiProvider, Provider<ArticleApi> articleApiProvider, Provider<CommentApi> commentApiProvider) { return new DataManagerModule_ProvideDataManagerFactory(module, authApiProvider, articleApiProvider, commentApiProvider); }
  • 31.
    Dagger 2. Debugging Hereshould be example of Guice stacktrace: VERY-VERY-BAD-STACKTRACE And here is Dagger stacktrace: Mmmm… What a lovely stacktrace!
  • 32.
    Dagger 2. Whatshould I inject? ● Anything that has constructor parameters ● Anything that is out of local scope ● Infrastructure ● Anything that is shared between >1 objects ● Different obj-graphs for different flavors
  • 33.
    Dagger 2. Whereshould I inject? public class MyApp extends Application { @Override public void onCreate() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { ... @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { inject(activity); } ... }); }
  • 34.
  • 35.
  • 36.