Channels ▼
RSS

Open Source

Google Guice


Choosing Between Implementations

The chef was obviously not pleased to figure out that the FortuneServiceImpl only had two fortunes to offer. To get some more variation in the messages, our chef subscribes to a second service, the MegaFortuneService, shown in Listing 6. Because the original subscription doesn't end until the end of the year, some way to choose between the two is necessary.

public class MegaFortuneService implements FortuneService {
private static final List<FortuneService> SERVICES =
   Arrays.<FortuneService>asList(
    new FunnyFortuneService(),
    new QuoteFortuneService()
    );
public String randomFortune() {
   int index = new Random().nextInt(SERVICES.size());
   return SERVICES.get(index).randomFortune();
   }
}
Listing 6: MegaFortuneService

Previously, the Guice knew which FortuneService to inject for Chef, because we had a binding in a Module implementation (see Listing 2). Common sense tells me to just add another binding for MegaFortuneService. Listing 7 shows what I came up with.

public class CommonSenseModule extends AbstractModule {
protected void configure() {
   bind(FortuneService.class).to(FortuneServiceImpl.class);
   bind(FortuneService.class).to(MegaFortuneService.class);
   }
}
Listing 7: Adding Another Binding: Does This Work?

You can easily modify the code in Listing 4 to create the Injector with the Module from Listing 7. However, when you do, you'll notice that Guice blows up at start-up. You'd see something like Listing 8.

Exception in thread "main" com.google.inject.CreationException:
Guice configuration errors:
1) Error at chapter2.CommonSenseModule.configure(CommonSenseModule.java:12):
A binding to chapter1.FortuneService was already configured at
chapter2.CommonSenseModule.configure(CommonSenseModule.java:11).
Listing 8: Oops, I Did It Again

Oh man, Guice doesn't like that. What did I forget? If you take a second look at the Chef constructor, shown in Listing 9, you'll see. Note: Guice's exceptions comprise a feature on their own. They go the extra mile and present you a human readable message and line numbers from your configuration code where appropriate. You'll be pleasantly surprised.

@Inject
 public Chef(FortuneService fortuneService) {
  this.fortuneService = fortuneService;
}
Listing 9: The Chef Constructor

Of course! Guice can't tell which FortuneService we need for Chef! So even if Guice would allow our CommonSenseModule bindings, using them would have never worked, because Guice has two FortuneService instances to choose from. I need some way to tell Guice, "Give me the MegaFortuneService!" without having to use the concrete implementations directly. Instead of letting you write more configuration, Guice solves this problem elegantly using binding annotations. Listing 10 provides an example.

@Inject
 public Chef(@Mega FortuneService fortuneService) {
  this.fortuneService = fortuneService;
 }
Listing 10: The Chef Constructor with a Binding Annotation

Now I can tell Guice which service is which in a module, as shown in Listing 11.

public class BindingAnnotationModule extends AbstractModule {
  protected void configure() {
   bind(FortuneService.class).to(FortuneServiceImpl.class);
   bind(FortuneService.class)
    .annotatedWith(Mega.class)
    .to(MegaFortuneService.class);
  }
}
Listing 11: A Module Using Binding Annotations

Again, my configuration remains highly readable, "Bind all requests for FortuneService annotated with @Mega to MegaFortuneService." One might hope that Guice provides these binding annotations for you. Alas, that wouldn't work for the same reason actors shouldn't go into politics: sometimes, you just have absolutely no idea what you're talking about. Similarly, Guice can't possibly know how to name your binding annotations, what their visibility should be, or where you would want to place them (in a constructor, method, or so on). That's why you need to create them yourself, much like in the @Mega example in Listing 12. Most binding annotations look very similar, so don't feel bad copying and pasting the boilerplate code. In fact, I even recommend doing so, because that means you're less likely to forget something.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Mega {}
Listing 12: @Mega Binding Annotation

Scary stuff.

To support this binding annotation functionality, Guice internally identifies all bindings with an instance of the Key class. A Key instance is a combination of a type (FortuneService) and an optional annotation type (@Mega). If you played around with the Injector a bit in the previous section, you probably noticed that the getInstance(...) method has an overload that takes a Key object instead of a Class one. There's only one Chef, but if you would like to get a MegaFortuneService directly, you would do so as in Listing 13.

injector.getInstance(Key.get(FortuneService.class, Mega.class));
Listing 13: Getting an Instance by its Key

If you call getInstance(...) with a Class, for example, Chef.class, Guice actually delegates to getInstance(Key) internally. So the two lines in Listing 14 are essentially the same.

injector.getInstance(Chef.class);
injector.getInstance(Key.get(Chef.class));
Listing 14: Get by Class or Key

Understanding that every single binding is internally represented by a Key object, including simple class bindings like Chef, will save you lots of time. Especially when you start using Guice's object lifetime support (scopes). I can't emphasize this enough, so repeat it after me: Every binding is represented by a Key, the whole Key and nothing but the Key, so help me Bob.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video