Binding Annotations¶
Occasionally, you may want multiple bound instances of the same type. For instance you may want both a FooHttpClient <: HttpClient and a BarHttpClient <: HttpClient.
To do this we recommend creating a specific binding annotation.
Define an Annotation¶
Defining a binding annotation is a few lines of java code plus imports. We recommend that you put the annotation in its own .java file.
package example.http.clients.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface FooClient {}
// Older code may still use Guice `com.google.inject.BindingAnnotation` in place of the standard
// `@Qualifier` annotation. New code should use `@Qualifier` instead.
@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface FooClient {}
For more information on these meta-annotations see the Google Guice documentation on Binding Annotations.
Create a Binding with the Annotation¶
In your Module annotate the @Provides
method that provides the specific instance with the Binding Annotation, e.g.,
object MyHttpClientsModule extends TwitterModule {
val fooClientDestination = flag("foo.client.dest", "Foo Client Destination")
val barClientDestination = flag("bar.client.dest", "Bar Client Destination")
@Singleton
@Provides
@FooClient
def providesFooHttpClient: HttpClient = {
val dest = fooClientDestination.get match {
case Some(value) => value
case _ => "DEFAULT"
}
new HttpClient(dest)
}
@Singleton
@Provides
@BarClient
def providesBarHttpClient: HttpClient = {
val dest = barClientDestination.get match {
case Some(value) => value
case _ => "DEFAULT"
}
new HttpClient(dest)
}
}
Depend on the Annotated Type¶
Then to depend on the annotated binding, just apply the annotation to the injected parameter:
class MyService @Inject()(
@FooClient fooHttpClient: HttpClient,
@BarClient barHttpClient: HttpClient) {
...
}
Benefits Over Using @Named Binding Annotation¶
You could also achieve the same behavior using the @Named binding annotation. However we’ve found that creating specific binding annotations avoids potential naming collisions.
Additionally, being able to find all usages of the annotation by type is beneficial over a text-search for the string used in @Named
.