Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice @Provides Methods vs Provider Classes

I'm working on a fairly large project that has a lot of injections. We're currently using a class that implements Provider for each injection that needs one, and they mostly have one line get methods.

It's starting to get annoying to create a new class every time I need a new provider. Is there any benefit to using provider classes over @Provides methods in my Module or vice-a-versa?

like image 880
Eliezer Avatar asked Jan 28 '15 21:01

Eliezer


People also ask

What is a provider in Google Guice?

Providers are used in numerous ways by Guice: When the default means for obtaining instances (an injectable or parameterless constructor) is insufficient for a particular binding, the module can specify a custom Provider instead, to control exactly how Guice creates or obtains instances for the binding.

How do you use a Guice provider?

Dependency Injection for Java Developers with Dagger & Guice As @provides method becomes more complex, this method can be moved to separate classes using Provider interface. Next, you have to map the provider to type. bind(SpellChecker. class).

What does @provides do in Java?

Dependency Injection for Java Developers with Dagger & Guice Guice provides a way to create bindings with complex objects using @provides method. This methods is being part of Binding Module and provides the complex object to be mapped.

What does injector getInstance do?

getInstance. Returns the appropriate instance for the given injection type; equivalent to getProvider(type). get() . When feasible, avoid using this method, in favor of having Guice inject your dependencies ahead of time.


1 Answers

As far as I know, they're exactly equivalent for most simple cases.

/**
 * Class-style provider.
 * In module: bind(Foo.class).annotatedWith(Quux.class).toProvider(MyProvider.class);
 */
class MyProvider implements Provider<Foo> {
  @Inject Dep dep;  // All sorts of injection work, including constructor injection.

  @Override public Foo get() {
    return dep.provisionFoo("bar", "baz");
  }
}

/**
 * Method-style provider. configure() can be empty, but doesn't have to be.
 */
class MyModule extends AbstractModule {
  /** Name doesn't matter. Dep is injected automatically. */
  @Provides @Quux public Foo createFoo(Dep dep) {
    return dep.provisionFoo("bar", "baz");
  }

  @Override public void configure() { /* nothing needed in here */ }
}

In either style, Guice lets you inject Foo and Provider<Foo>, even if the key is bound to a class or instance. Guice automatically calls get if getting an instance directly and creates an implicit Provider<Foo> if one doesn't exist. Binding annotations work in both styles.

The main advantage of @Provides is compactness, especially in comparison to anonymous inner Provider implementations. Note, however, that there might be a few cases where you'd want to favor Provider classes:

  • You can create your own long-lived Provider instances, possibly with constructor parameters, and bind keys to those instances instead of to class literals.

    bind(Foo.class).toProvider(new FooProvisioner("bar", "baz"));
    
  • If you're using a framework compatible with JSR 330 (javax.inject), you can easily bind to javax.inject.Provider classes or instances. com.google.inject.Provider extends that interface.

    bind(Foo.class).toProvider(SomeProviderThatDoesntKnowAboutGuice.class);
    
  • Your Provider may be complex enough to factor into its own class. Depending on how you've structured your tests, it may be easier to test your Provider this way.

  • Providers can extend abstract classes. It may not be easy or intuitive to do this with @Provides methods.

  • You can bind several keys to the same Provider directly. Each @Provides method produces exactly one binding, though you could bind other keys to the key (@Quux Foo here) and let Guice do a second lookup.

  • Providers are easy to decorate or wrap, if you wanted to (for instance) cache or memoize instances without using Guice scopes or bindings.

    bind(Foo.class).toProvider(new Cache(new FooProvisioner("bar", "baz")));
    

IMPORTANT: Though this is a good strategy for classes that Guice can't create, bear in mind that Guice can automatically create and inject a Provider<T> for any T that you bind in any way, including to a class name, key, or instance. No need to create an explicit provider unless there's actual logic of your own involved.

like image 122
Jeff Bowman Avatar answered Oct 25 '22 02:10

Jeff Bowman