@ImplementedBy
As I mentioned in the previous blog entries, you configure Google Guice by creating a class that implements the Module interface or better, one that extends AbstractModule class. In this class you specify the bindings that logically connects implementations to interfaces. However, there’s a way that lets you create the bindings using annotations without needing to write the code in a Module implementation. You can do that using the @ImplementedBy annotation decorating an interface.
Let’s see a quick example:
@ImplementedBy(MobilePhone.class)
public interface Phone {
public void ring();
}
The simple interface above is decorated with the @ImplementedBy annotation and the name of the implementer is specified as a single attribute.
public void ring() {
System.out.println(“It’s playing some annoying melody…”);
}
}
The MobilePhone class, which is the Phone implementation in our simple example, is shown above. The @ImplementedBy annotation will tell Google Guice to create a MobilePhone instance when a Phone is required.
public class PhoneModule extends AbstractModule {
@Override
public void configure() {
}
}
As you can see, the PhoneModule is empty. It doesn’t explicitly specify any bindings.
import com.google.inject.Injector;
public class App {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new PhoneModule());
Phone aPhone = injector.getInstance(Phone.class);
aPhone.ring();
}
}
If you run the application above, you’ll see that a MobilePhone instance is created and its ring() method is invoked.
Let’s create a new Phone implementation! We’ll call this one SatellitePhone:
public class SatellitePhone implements Phone {
public void ring() {
System.out.println(“…Satellite Ring Tone…”);
}
}
And let’s specify this binding in our module class:
public void configure() {
bind(Phone.class).to(SatellitePhone.class);
}
…
The instruction above specifies that if we request a Phone instance, Guice is going to provide us with a SatellitePhone instance. But what about the existing @ImplementedBy(MobilePhone.class) annotation that we added in the Phone interface? Let’s see what happens when we run the application again!
So, the explicit binding instruction given in the module file overrides the @ImplementedBy annotation. This means that you can declare a default implementation using the @ImplementedBy annotation and you can overwrite it if necessary in a module class.
Annotating Binding
But what if we need to use two Phone implementations in a scenario like the following one?
Let’s say we have a User class that has two phone attributes: A mobile phone and a satellite phone, respectively implemented by MobilePhone class and SatellitePhone class as shown earlier on.
import com.google.inject.Inject;
public class User {
private Phone mobile;
private Phone satellite;
public Phone getMobile() {
return mobile;
}
@Inject
public void setMobile(Phone mobile) {
this.mobile = mobile;
}
public Phone getSatellite() {
return satellite;
}
@Inject
public void setSatellite(Phone satellite) {
this.satellite = satellite;
}
}
How are we going to configure Guice so that the right injection happens? I modified the application slightly:
Injector injector = Guice.createInjector(new PhoneModule());
User user = injector.getInstance(User.class);
user.getMobile().ring();
user.getSatellite().ring();
}
When I run the application as it is, it’s going to give us this:
> …Satellite Ring Tone…
Of course, whenever I ask for a Phone instance, I get a MobilePhone instance. Can I use two @ImplementedBy(…) instructions?
@ImplementedBy(SatellitePhone.class)
public interface Phone {
Nope! This wouldn’t work…
So I turn to the Module file in order to create a configuration:
bind(Phone.class).to(MobilePhone.class);
bind(Phone.class).to(SatellitePhone.class);
}
But this doesn’t even compile. The compiler says:
So, clearly I cannot create a second binding this way. In this situation, binding annotations will help us. I make a little change to the module class so that Guice expects annotations, just as a marker interfaces, to differentiate two implementations.
bind(Phone.class).annotatedWith(Mobile.class).to(MobilePhone.class);
bind(Phone.class).annotatedWith(Satellite.class).to(SatellitePhone.class);
}
Then, I create the annotations:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import com.google.inject.BindingAnnotation;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Satellite { }
and
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import com.google.inject.BindingAnnotation;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Mobile { }
The last step is to modify the User class so that Guice knows what to inject in which place:
public void setMobile(@Mobile Phone mobile) {
this.mobile = mobile;
}
…
@Inject
public void setSatellite(@Satellite Phone satellite) {
this.satellite = satellite;
}
We compile (or save in Eclipse) and run. And this time it works as expected:
> …Satellite Ring Tone…
In the next and the last part of these articles I’m going to deal with custom providers and I’m going to give my humble opinion about Google Guice.
Take care for now!
- Yagiz -
Technorati Tags: Google Guice
Step wise explanation of the entire code would be appreciated by any newbie in Guice….”Juice”..
Good Job!!
Well done. Presentation is excellent and the example is very easy to understand,