Sorry if it's a trivial question, I am new to Guice. Let's say I have the following class:
public class MyClass {
@Inject
public MyClass(final MyDependency myDependency) {
this.name = myDependency.getName();
}
public String getName() {
this.name;
}
}
Then somewhere else I want to do:
public class SomeOtherClass {
public void test() {
MyClass instanceFoo = injector.getInstance(MyClass.class);
MyClass instanceBar = injector.getInstance(MyClass.class);
assertTrue("foo", instanceFoo.getName());
assertTrue("bar", instanceBar.getName());
}
}
I would like to have two instances of "MyClass", one which has a name of "foo" and one with a name of "bar" (i.e. each of them has a different instance of it's dependency as well). How can I add those 2 instances to my injector, and how can I retrieve each of them with "injector.getInstance"?
I don't want to create a subclass of "MyClass" for each possible dependency.
Thanks!
A program may create many objects of the same class. Objects are also called instances, and they can be stored in either a named variable or in an array or collection.
Guice comes with a built-in binding annotation @Named that takes a string: public class RealBillingService implements BillingService { @Inject public RealBillingService(@Named("Checkout") CreditCardProcessor processor, TransactionLog transactionLog) { ... }
Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.
Annotation Type Inject. @Target(value={METHOD,CONSTRUCTOR,FIELD}) @Retention(value=RUNTIME) @Documented public @interface Inject. Annotates members of your implementation class (constructors, methods and fields) into which the Injector should inject values.
Your question, as in the title, has a simpler solution: If you want an arbitrary number of instances of MyClass or MyDependency, you can inject a Provider<MyClass>
or Provider<MyDependency>
. This is true whether or not you've actually bound a Provider in a Module; for any T available in the graph Guice can actually inject either T
or Provider<T>
. (These correspond to Injector.getInstance
and Injector.getProvider
respectively.)
As in the body of the question, there are two pieces: Making more than one injectable Key
for the same Class
, and setting up the instances the way you want with different names and injected dependencies.
Guice identifies bindings using a Key
, which is a fully-qualified class (like MyClass
or List<MyClass>
) combined with an optional "binding annotation". This is an annotation that is itself annotated with BindingAnnotation
or javax.inject.Qualifier
; you can create your own, or use the built in one called @Named
that takes a String (so @Named("foo")
is different from @Named("bar")
).
Most of the time, you can get by without using Key directly: Use annotatedWith
in your bind
calls or add the annotation onto @Provides
methods, and request them by putting the annotation onto constructor parameters or @Inject-annotated fields. However, you can still use getInstance
and getProvider
by creating a Key manually, using the static methods on Key. (For complicated cases, use TypeLiteral or Names.named
; see their docs for details.)
Now that you know how to inject @Named("foo") MyClass
or @Foo MyClass
, how do you provide them? Depending on your needs, I'd choose one of three options: binding toInstance
, using a @Provides
method, or creating an "assisted injection" factory.
If your MyClass instances don't need injection themselves, and you don't mutate or manipulate instance state, you could just prepare the instances named as needed and then bind
them with toInstance
.
You could also write a @Provides @Named("foo") MyClass
method that takes a MyClass
parameter (which Guice supplies through the injector), sets the name, and returns the instance. This is a low-overhead alternative to writing a Provider class or instance, and will get you a fresh instance rather than the sharing that a toInstance
binding would imply.
If you really want the name
to be a part of your class's constructor parameters, perhaps to keep the instance immutable, you can use "Assisted Injection" to tell Guice which parameters you supply yourself and which come from the Guice injector. This would allow you to inject a MyClass.Factory
and call myClassFactory.create("foo")
, which you could do directly in your consuming classes, or using the @Provides
technique above. The details are a little beyond the scope of the question, but look up "Assisted Injection" for details on syntax and on adding the appropriate JAR.
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