Why Dependency Injection
- don’t create it, ask for it.
- decouples classes
- make unit testing easier
Guice vs Dagger
Guice is base on reflection, while Dagger is generating extra code during compile-time; reflection is much slower than the pre-generated code
Types of Dependency Injection
- Constructor Injection
- preferred: as it minimize mutability
- use private final fields.
- Method Injection
- Don’t use Field Injection
- difficult to test
- field can’t be final
AbstractModule
bind(Interface.class).to(Implementation.class)
bind().toInstance()
Injector
- One injector per application.
Injector injector = Guice.createInjector(new XyzModule());
Xyz xyz = injector.getInstance(Xyz.class);
@Qualifier
- Prefer @Qualifier over old @BindingAnnotation
- Used when need bind a type to multiple classes or instances
// Step `: declare the annotation`
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@interface AnnotationName {}
// Step 2: bind the annotation with value in configure or use @Provides method
bind(String.class).annotatedWith(AnnotationName.class).toInstance("SomeString");
@Provides @Singleton @AnnotationName
String provideSomeString() { return "SomeString"; }
// Step 3: inject
@Inject
SomeClass(@AnnotationName String someString) {...}
@Named
- discouraged due to potential typos, difficult to refactor: change the names, can’t restrict the access
- prefer custom @Qualifier annotations to @Named
@Named("Checkout") CreditCardProcessor processor;
// in the Module
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
@Provides Methods
- @Provides methods can have parameters that are injected by Guice
- @Provides methods will be called each time the dependency is requested
@Singleton
- By default, Guice will create a new instance for each dependency it injects
- Avoid singleton when possible
- To create a singleton instance:
- Add @Singleton to the class or the @Provides method
- bind().to().in(Singleton.class);
@RequestScoped, @SessionScoped
- Don’t use scope unless have to
Implicit binding vs explicit binding
- Bindings present in binding module are called explicit bindings and are of higher precedence.
- just-in-time bindings are called implicit bindings. If both type of bindings are present, explicit bindings are considered for mapping.
- a non-private, no-arguments constructor, or a constructor with the @Inject annotation
- @ImplementedBy
- @ProvidedBy
Best Practices
- Inject only direct dependencies
- Avoid null, Use @Nullable if have to
- Guice forbids null by default. It will refuse to inject null,
- Avoid Injecting Closable Resources
- Keep constructors on Guice-instantiated classes as hidden as possible.
- limit the visibility of both your implementation classes, and their constructors.
- Use package private not public.
- Modules should be fast and side-effect free
- Avoid IO, create connection.
- Avoid conditional logic in modules
- One module per package
- One public module per top-level feature
- Organize modules vertically, not horizontally
- Publish implementation as a Guice module
- Use annotations from javax.inject package
- Document the public bindings provided by modules
- Don’t reuse annotations