问题
Is there a way to tell Dagger 2 how to provide something, but not allow it to be injected?
Say I want to inject a Qux
. A Qux
requires a Foo
and a Bar
:
@Provides
@Singleton
Foo foo() {
return new Foo();
}
@Provides
@Singleton
Bar bar() {
return new Bar();
}
@Provides
@Singleton
Qux qux(final Foo foo, final Bar bar) {
return new Qux(foo, bar);
}
But what if I don't want Foo
and Bar
to be injectable? Perhaps exposing them would break the encapsulation of the Qux
, or perhaps they're factories of some kind that I only want the Qux
to have access to.
I've thought of a couple ways I could achieve this:
- If the
Foo
singleton is needed by other providers, I could make it a class member. But this would get messy ifFoo
has several dependencies of its own. - If the
Bar
singleton is not needed by any other providers, I could create the instance inside theQux
provider. But this would get messy ifQux
has several dependencies like that.
Neither of these solutions seem very elegant or Daggery. Is there another way?
回答1:
The easiest way to achieve what you're trying to do is to define a package-private qualifier.
package my.pkg;
@Qualifier
@Retention(RUNTIME)
@interface ForMyPackage {}
Then, use that for your bindings for Foo and Bar:
@Provides
@Singleton
@ForMyPackage
Foo foo() {
return new Foo();
}
@Provides
@Singleton
@ForMyPackage
Bar bar() {
return new Bar();
}
@Provides
@Singleton
Qux qux(final @ForMyPackage Foo foo, final @ForMyPackage Bar bar) {
return new Qux(foo, bar);
}
That way, you can only request that those versions of Foo and Bar be injected if you have access to the qualifier.
If all of these bindings are in a single module, you can even use a private, nested qualifier in the module.
Edit by asker:
I tried the last suggestion.
@Qualifier
@Retention(RUNTIME)
private @interface NoInject {}
@Provides
@Singleton
@NoInject
Foo foo() { return new Foo(); }
Attempting to inject a Foo
causes a compile-time error, as desired:
Error:(15, 6) Gradle: error: com.mydomain.myapp.Foo cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method. com.mydomain.myapp.MyActivity.foo [injected field of type: com.mydomain.myapp.Foo foo]
So while the error message is a bit misleading, this technique is neat and effective.
回答2:
I'd say this sounds a lot like component dependencies.
[..] As an alternative, components can use bindings only from another component interface by declaring a component dependency. When a type is used as a component dependency, each provision method on the dependency is bound as a provider. Note that only the bindings exposed as provision methods are available through component dependencies.
If you really want to hide your Foo
and Bar
, you could make the module, Foo
, and Bar
package local. Then create a component that only exposes Qux
and use it as a component dependency.
Since Foo
and Bar
are package local, a SubComponent
should not be able to use them, either.
Your component would be the only one able to use the module and thus create and use Foo
and Bar
in its dependency graph.
来源:https://stackoverflow.com/questions/39219257/can-a-dagger-2-dependency-be-non-injectable