-
Notifications
You must be signed in to change notification settings - Fork 1.7k
BindingAnnotations
Occasionally you'll want multiple bindings for the same type. For example, you might want both a PayPal credit card processor and a Google Checkout processor. To enable this, bindings support an optional binding annotation. The annotation and type together uniquely identify a binding. This pair is called a key.
Binding annotations are Java annotations that are annotated with meta annotation
@Qualifier
or @BindingAnnotation
.
Defining a binding annotation requires two lines of code plus several imports.
Put this in its own .java
file or inside the type that it annotates.
package example.pizza;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface PayPal {}
// Older code may still use Guice `BindingAnnotation` in place of the standard
// `@Qualifier` annotation. New code should use `@Qualifier` instead.
@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface GoogleCheckout {}
You don't need to understand all of these meta-annotations, but if you're curious:
-
@Qualifier
is a JSR-330 meta-annotation that tells Guice that this is a binding annotation. Guice will produce an error if multiple binding annotations are ever applied to the same member. Guice's@BindingAnnotation
is also used for this purpose in older code. - The
@Target({FIELD, PARAMETER, METHOD})
annotation prevents the@PayPal
annotation from accidentally being applied in places where it serves no purpose. -
@Retention(RUNTIME)
makes the annotation available at runtime, which is required in Guice.
To depend on the annotated binding, apply the annotation to the injected parameter:
public class RealBillingService implements BillingService {
@Inject
public RealBillingService(@PayPal CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}
Lastly we create a binding that uses the annotation:
final class CreditCardProcessorModule extends AbstractModule {
@Override
protected void configure() {
// This uses the optional `annotatedWith` clause in the `bind()` statement
bind(CreditCardProcessor.class)
.annotatedWith(PayPal.class)
.to(PayPalCreditCardProcessor.class);
}
// This uses binding annotation with a @Provides method
@Provides
@GoogleCheckout
CreditCardProcessor provideCheckoutProcessor(
CheckoutCreditCardProcessor processor) {
return processor;
}
}
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) {
...
}
To bind a specific name, use Names.named()
to create an instance to pass to
annotatedWith
:
final class CreditCardProcessorModule extends AbstractModule {
@Override
protected void configure() {
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
}
}
Since the compiler can't check the string, we recommend using @Named
sparingly. Defining your own purpose-built annotations provides better
type-safety.
Guice supports binding annotations that have attribute values (like @Named
).
In the rare case that you need such an annotation (and can't use an @Provides
method) we encourage you to use
@AutoAnnotation
from the Auto/Value project, as properly implementing an annotation is
error-prone. If you do decide to manually create a custom implementation be sure
to properly implement the equals()
and hashCode()
specifications detailed in
the
Annotation
Javadoc.
Pass an instance of this class to the annotatedWith()
binding clause.
-
User's Guide
-
Integration
-
Extensions
-
Internals
-
Releases
-
Community