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?
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.