I haven't managed to find a good explanation/example on method injection using Dagger 2. Could someone please help me understand?
Example:
@Inject
public Dinner makeDinner(Pasta pasta, Sauce sauce) {
mPan.add(pasta);
mPan.add(sauce);
return mPan.cookDinner();
}
So if I annotate my method with @Inject
, am I correct to assume that the arguments in the method signature will be injected with defined objects from the object graph? How can I use this method in my code then? It will still expect me to supply all the arguments, when I make the method call, which sort of defeats the purpose.
UPDATE:
So from what I understand the Dinner object will be available if I call DinnerComponent.dinner()
, assuming my DinnerComponent is set up like this:
@Component(modules = DinnerModule.class)
public interface DinnerComponent {
Dinner dinner();
}
and my DinnerModule is set up like this:
@Module
public class DinnerModule {
public DinnerModule() {}
@Provides
Pasta providePasta() { return new Pasta(); }
@Provides
Sauce provideSauce() { return new Sauce(); }
}
What happens if I want my dinner fried? So let's introduce this method:
@Inject
public Dinner makeDinner(Pasta pasta, Sauce sauce) {
mPan.add(pasta);
mPan.add(sauce);
return mPan.fryDinner();
}
How can I specify within the component which dinner is which?
Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier version created by Square and now maintained by Google.
Dagger automatically generates code that mimics the code you would otherwise have hand-written. Because the code is generated at compile time, it's traceable and more performant than other reflection-based solutions such as Guice. Note: Use Hilt for dependency injection on Android.
Dagger constructs instances of your application classes and satisfies their dependencies. It uses the javax. inject. Inject annotation to identify which constructors and fields it is interested in. Use @Inject to annotate the constructor that Dagger should use to create instances of a class.
One fundamental difference about Dagger method injection, compared to the way you are using it, is that Dagger method injection is just another way for Dagger to send in dependencies when constructing or injecting a DI-ready object, which means that @Inject-annotated methods are meant to be called by Dagger once on construction and not from within your own code. This makes it very very unlikely that you would @Inject
-annotate makeDinner
, fryDinner
, or any other method that has meaningful side effects or return values. Instead, treat method injection as a post-construction opportunity for constructor-style injection.
(Of course, you can always practice general dependency injection at the method level, passing your dependencies into a method call so that the method itself does not have to create them. However, this is not what Dagger means with its definition of method injection, and it does not help support that case.)
public class Chef {
private Provider<Pasta> mPastaProvider;
private Sauce mSauce;
@Inject
public void registerIngredients( // can be named anything
Provider<Pasta> pastaProvider,
Sauce sauce) { // T and Provider<T> both work, of course
mPastaProvider = pastaProvider;
mSauce = sauce;
}
/* Non-@Inject */ public Dinner cookDinner() {
mPan.add(mPastaProvider.get());
mPan.add(mSauce);
return mPan.cookDinner();
}
/* Non-@Inject */ public Dinner fryDinner() {
mPan.add(mPastaProvider.get());
mPan.add(mSauce);
return mPan.fryDinner();
}
}
In this case, when you request injection on a Chef instance, Dagger will see the @Inject-annotated method and call it with arguments from the Dagger graph.
This works with or without Chef being Dagger-constructable: Unless you have an @Inject-annotated constructor or @Provides method, you won't be able to get a Chef directly from your Component, but you could create a void
method on the Component which receives an already-constructed Chef
instance. That Component method would use field and method injection to provide that Chef with the ingredients, Providers, Optionals, or Lazys they may need. See the @Component and MembersInjector docs for details.
Note that in no case does Dinner
appear available on the object graph! Adding @Inject to a method or field merely tells Dagger that as part of the injection process it should populate that field or call that method with the given dependencies. If you want to make a Dinner available on the object graph, you'll need to @Inject-annotate the Dinner constructor, or put a @Provides or @Binds method on a Module that you feed into the Component.
Why would you use method injection? Though constructor injection is preferable, and allows the class's fields to be final
, consider a case where objects are created reflectively (e.g. Activities, Fragments, and Views in Android, or Serializable objects). Field injection (where Dagger populates an @Inject
-annotated field) would work as well, but in some cases you may prefer not to expose @Inject
-annotated fields. In those cases, you could work around constructor constraints by having your injection happen on an @Inject
-annotated method. Similarly, though I haven't tried this, you could take advantage of class hierarchy to mark an interface method with @Inject: this would ensure that whether or not you're in a DI context you can pass certain dependencies to an object as part of their preparation.
Annotating a method with @Inject
gives Dagger instructions to execute this method right after the object's creation — right after the constructor call. This is useful when you need a fully constructed object for something. There is an example of method injection in this article.
You are right by saying that this method's parameters will be supplied by Dagger, that's why you are not supposed to call this method by yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With