Search code examples
guice

Combining @Provides and @Inject


I've recently come across some code that uses both @Provides and @Inject annotations on the same method. This method has two non-primitive parameters and a non-void return type.

I was wondering whether it made sense to use these two in tandem. From what I can gather/speculate, it seems like @Inject is being used to construct the method dependencies using Guice, whereas @Provides is being used to bind the return type. Any ideas would be appreciated.


Solution

  • No, there is never a case where you'd see @Provides and @Inject on the same method.

    @Inject makes sense on constructors, methods, and fields:

    • On constructors, it marks the constructor that the DI framework should call. The framework provides all parameter values. This is often called constructor injection.
    • On fields, it indicates that the DI framework should set the field from outside. The framework provides the field value. This is often called field injection.
    • On methods, it indicates that after construction the DI framework should call the method exactly once. The framework provides the parameter values. The return value is meaningless and often void. This is often called method injection.

    For example:

    public class YourInjectableClass {
    
      // Guice will use this constructor...
      @Inject public YourInjectableClass(YourDep dep) { /* ... */ }
      // ...instead of this one
      public YourInjectableClass(YourDep dep, YourOtherDep otherDep) { /* ... */ }
    
      // Guice will populate this field after construction.
      @Inject YourField yourField;
    
      @Inject public void register(Registry registry) {
        // Guice will create or get a Registry and call this method after
        // construction. It sometimes makes sense for this to be protected
        // or package-private so this class's consumers aren't tempted
        // to call it themselves.
      }
    }
    

    @Provides has a much narrower purpose: When used in Guice modules, @Provides goes on methods and indicates that Guice should call the method whenever it needs an instance of the method's possibly-parameterized type (and inheriting the binding annotations or qualifiers from the method itself). Dagger has a similar @Provides annotation, as @Provides isn't defined in the JSR-330 dependency injection standard.

    For both Guice and Dagger, the framework will supply the @Provides method's parameters whenever the method is called, and without using @Inject. The semantics of @Inject wouldn't make sense anyway, since even though the parameters are supplied in a similar way to method injection above, the framework may call the @Provides method repeatedly but @Inject methods are called exactly once.

    public class YourModule extends AbstractModule {
      @Override public void configure() {)  // still needed for AbstractModule
    
      // Guice will call this whenever it needs a
      // @SomeBindingAnnotation YourInstance. Because you list a YourDep as a
      // parameter, Guice will get and pass in a YourDep.
      @Provides @SomeBindingAnnotation YourInstance create(YourDep dep) {
        return YourInstanceFactory.create(dep);
      }
    }
    

    Consequently, you should almost never see those annotations in the same file, let alone on the same method. The only exception is if you follow the questionable practice of making a Module that is itself injectable (presumably getting an injected Module from one injector in order to configure a different injector), and even then you wouldn't see them on the same method: @Inject methods are called once to store dependencies, and @Provides methods should be called whenever a new instance is needed.