Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Migrating a Guice-based project to Dagger

I have a Guice based project using vanilla Guice; no Assisted-Inject, no AOP, no extra plugin extending Guice, etc. To run it more easily on Android, Dagger seems like a better solution. Every class has a dependency and a constructor with @Inject annotation. No field or method injection is used.

The modules are quite simple (making Guice an overkill) and mostly contain bindings like the following:

class SomethingModule extends AbstractModule {

  protected void configure() {
    Bind(Handler.class)
      .annotatedWith(Names.named("something"))
      .to(SomeImplementation.class);
    }
  }

}

And later used like the following:

Injector inj = Guice.createInjector(new SomethingModule());
... = inj.getInstance(SampleInterface.class);
// and rest of the code.

Unfortunately, I can not get my head around Daggers terminology. Can you guide me with a direct translation / transformation of a Guice module to a Dagger module?

Dagger has:

  • Dagger's Components.
  • Dagger's Modules.
  • @Provides
  • @Inject

Guice has:

  • @Inject
  • @Named (or any custom annotation, if implemented correctly).
  • Our modules extending AbstractModule.
  • @Provides in the modules.
  • Guice Injector created from modules.

How do these relate?

Update: In addition to the nice answer by EpicPandaForce, these slides can help too.

like image 328
hkoosha Avatar asked Apr 07 '16 17:04

hkoosha


People also ask

Why is dagger faster than Guice?

However, the biggest difference is that Dagger does all the heavy lifting at compile time (which means you do the work once, no matter how many times you run it), whereas Guice must do the equivalent work every time the application starts up.

What does @inject do Guice?

Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.

What is @named annotation in Guice?

Guice provides another way also to map bindings without creating a custom annoation. It allows so using @Named annotation.

What does bind do in Guice?

Guice Basic Bindings. Binding is to Guice as wiring is to Spring. With bindings, we define how Guice is going to inject dependencies into a class. This module implementation specifies that an instance of DefaultCommunicatorImpl is to be injected wherever a Communicator variable is found.


1 Answers

Bind(Handler.class)
.annotatedWith(Names.named("something"))
.to(SomeImplementation.class);

Would translate to

@Module
public class SomethingModule {
    @Provides
    @Named("something")
    //scope if needed
    public Handler handler() {
        return new SomeImplementation();
    }
}

Which would be bound to an "Injector" (component):

@Component(modules={SomethingModule.class})
//scope if needed
public interface SomethingComponent {
    @Named("something")
    Handler handler();

    void inject(ThatThingy thatThingy);
}

Which is an "injector" that you have to create with the APT-generated builder:

SomethingComponent somethingComponent = DaggerSomethingComponent.builder()
                                           .somethingModule(new SomethingModule()) //can be omitted, has no params
                                           .build();
somethingComponent.inject(thatThingy);

Where that thingy has

public class ThatThingy {
    @Inject
    @Named("something")
    Handler handler;
}

Components typically exist per scope, so for example @ApplicationScope has one "injector" (component). Scoping can be achieved with subcomponents and component dependencies.

Important fact, a component has provision methods (which are the dependencies that are inherited to subscoped components if you use component dependencies), and void inject(X x); formatted methods. This is required for field injection per concrete type. A base class for example can only inject itself, and not its subclasses. You can however write a method called protected abstract void injectThis() which would call the .inject(this) on the subclass as well.

As I haven't really used Guice, I'm not sure if I missed out on anything. I think I forgot constructor injection, which is an issue because while Dagger does support it, it cannot be reconfigured. For reconfiguration, you have to use modules, and do the injection in the constructors yourself.

@Module(includes={ThoseModule.class, TheseModule.class})
public abstract class SomethingModule {
    @Binds
    abstract Whatever whatever(WhateverImpl impl);
}

@Singleton
public class WhateverImpl implements Whatever {
    Those those;
    These these;

    @Inject
    public Whatever(Those those, These these) {
        this.those = those;
        this.these = these;
    }
}

@Component(modules={SomethingModule.class})
@Singleton
public interface SomethingComponent {
    These these();
    Those those();
    Whatever whatever();
}
like image 82
EpicPandaForce Avatar answered Sep 23 '22 00:09

EpicPandaForce