My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
BindingAnnotations  
Updated Oct 16, 2011 by sberlin

Binding Annotations

Occasionally you'll want multiple bindings for a 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.

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 com.google.inject.BindingAnnotation;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface PayPal {}

You don't need to understand all of these meta-annotations, but if you're curious:

  • @BindingAnnotation tells Guice that this is a binding annotation. Guice will produce an error if ever multiple binding annotations apply to the same member.
  • @Target({FIELD, PARAMETER, METHOD}) is a courtesy to your users. It prevents @PayPal from being accidentally being applied where it serves no purpose.
  • @Retention(RUNTIME) makes the annotation available at runtime.

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. This uses the optional annotatedWith clause in the bind() statement:

    bind(CreditCardProcessor.class)
        .annotatedWith(PayPal.class)
        .to(PayPalCreditCardProcessor.class);

@Named

Guice comes with a built-in binding annotation @Named that uses 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:

    bind(CreditCardProcessor.class)
        .annotatedWith(Names.named("Checkout"))
        .to(CheckoutCreditCardProcessor.class);

Since the compiler can't check the string, we recommend using @Named sparingly.

Binding Annotations with Attributes

Guice supports binding annotations that have attribute values. In the rare case that you need such an annotation:

  1. Create the annotation @interface.
  2. Create a class that implements the annotation interface. Follow the guidelines for equals() and hashCode() specified in the Annotation Javadoc. Pass an instance of this to the annotatedWith() binding clause.

Comment by jchwasto...@gmail.com, Aug 28, 2009

You probably wanted:

bind(CreditCardProcessor.class)
     .annotatedWith(Names.named("Checkout"))
     .to(CheckoutCreditCardProcessor.class);
         ^^^^^^^^

in here.

Comment by shonzi...@gmail.com, Sep 23, 2009

jchwastowska is right!

Is there any point in specifying a concrete type if the target instance's type is already determined using string value of @Named?

Could this be used as some sort of implicit fail-over mechanism in case there's nothing (instantiable) defined using string value of @Named?

Comment by Cessatio...@gmail.com, May 30, 2011

I would love to see a feature where a child object can bind to a named instance depending on the named instance of its parent object. So I can name a parent object something and all of the fields inside of that parent object that have a @NamedLikeParent? annotation will Inject as though they had the parent's name.

Comment by bposs...@gmail.com, Jun 20, 2011

Binding annotations seem to run contrary to dependency injection and programming to interfaces. RealBillingService? depends on CreditCardProcesser?. The @PayPal? binding annotation essentially couples the RealBillingService? to the concrete PayPalCreditCardProcessor?. At this point i feel you may as well just make the RealBillingService? depend on the PayPalCreditCardProcessor? directly.

Comment by project member mccu...@gmail.com, Jun 21, 2011

Binding annotations (also known as Qualifiers in JSR330) let you bind multiple implementations of the same interface and distinguish between them. While it may look that depending on "@PayPal? CreditCardProcessor?" is the same as depending directly on the PayPalCreditCardProcessor? implementation, it isn't because using the interface means that I can bind a different implementation to "@PayPal? CreditCardProcessor?" for testing purposes - or for additional auditing purposes, etc.

Depending on PayPalCreditCardProcessor? directly means I can only inject that, or a sub-class of it, which is a much tighter constraint.

Comment by wudong....@gmail.com, Sep 15, 2011

Is there a way to get the annotated binding instance through the Injector interface?

Comment by mattmccu...@google.com, Nov 3, 2011

wudong.liu: injector.get(Key.get(CreditCardProcessor.class, Names.named("Checkout")))


Sign in to add a comment
Powered by Google Project Hosting