Dagger 2. Right way to do Dependency Injections
by Anton Minashkin
Usual JAVA codepublic 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 codepublic 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 codepublic 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 DIpublic 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 DIpublic 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-330public class Payroll { … @Inject public Payroll(EmployeeDatabase employeeDatabase) { mEmployeeDatabase = employeeDatabase; }…}
ORpublic 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 SquareAdopted 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● Diferent obj-graphs for diferent flavors
Dagger 2. Where should I inject?public class MyApp extends Application {@Overridepublic void onCreate() {registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {... @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { inject(activity); }...});}
Dagger 2
Questions?
Recommended