Friday, May 1, 2015

Google DI Guice - @ImplementedBy

The content below is taken from here:
http://blog.decaresystems.ie/2007/06/14/juicy-code-with-google-guice-part-4/
All parts about GUICE from the same source:
Part 2 – First Code
Part 3 – Dependency Injection
Part 4 – @ImplementedBy and Annotating Bindings
Part 5 – Custom Providers and Conclusions
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:
1
2
3
4
5
6
7
8
import com.google.inject.ImplementedBy;
@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.
1
2
3
4
5
6
7
public class MobilePhone implements Phone {
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.
1
2
3
4
5
6
7
8
import com.google.inject.AbstractModule;
public class PhoneModule extends AbstractModule {
@Override
public void configure() {
}
}
As you can see, the PhoneModule is empty. It doesn’t explicitly specify any bindings.
1
2
3
4
5
6
7
8
9
10
11
12
import com.google.inject.Guice;
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.
1
> It's playing some annoying melody...
Let’s create a new Phone implementation! We’ll call this one SatellitePhone:
1
2
3
4
5
6
7
8
9
package yagiz.test.guice.phone;
public class SatellitePhone implements Phone {
public void ring() {
System.out.println("...Satellite Ring Tone...");
}
}
And let’s specify this binding in our module class:
1
2
3
4
5
...
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 aSatellitePhone 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!
1
> ...Satellite Ring Tone...
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package yagiz.test.guice.phone;
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:
1
2
3
4
5
6
7
public static void main(String[] args) {
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:
1
2
> ...Satellite Ring Tone...
> ...Satellite Ring Tone...
Of course, whenever I ask for a Phone instance, I get a MobilePhone instance. Can I use two@ImplementedBy(…) instructions?
1
2
3
@ImplementedBy(MobilePhone.class)
@ImplementedBy(SatellitePhone.class)
public interface Phone {
Nope! This wouldn’t work…
So I turn to the Module file in order to create a configuration:
1
2
3
4
public void configure() {
bind(Phone.class).to(MobilePhone.class);
bind(Phone.class).to(SatellitePhone.class);
}
But this doesn’t even compile. The compiler says:
1
> A binding to yagiz.test.guice.phone.Phone was already configured at ...
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.
1
2
3
4
public void configure() {
bind(Phone.class).annotatedWith(Mobile.class).to(MobilePhone.class);
bind(Phone.class).annotatedWith(Satellite.class).to(SatellitePhone.class);
}
Then, I create the annotations:
1
2
3
4
5
6
7
8
9
10
11
12
13
package yagiz.test.guice.phone;
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
1
2
3
4
5
6
7
8
9
10
11
12
13
package yagiz.test.guice.phone;
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:
1
2
3
4
5
6
7
8
9
@Inject
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:
1
2
> It's playing some annoying melody...
> ...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!

No comments: