Dependency Injection with Google Guice by example

In this blogpost, I’ll explain Dependency Injection with Google Guice through a lot of examples. Hope you like it.
Annotation based DI
It’s possible in Guice to inject a class with the help of annotations. In this example, I create a mainclass which creates an order. Inside the order there will be a payment done. There are 2 types of payments (by card or cash) and they will be injected by Guice.
Payment
public interface Payment {
public void pay();
}
PaymentCash
public class PaymentCashImpl implements Payment {
public void pay() {
System.out.println("I'll pay just plain cash");
}
}
PaymentCard
public class PaymentCardImpl implements Payment{
public void pay() {
System.out.println("I'll pay with a credit card");
}
}
Order
public class Order {
private Payment payment;
public Payment getPayment() {
return payment;
}
public void setPayment(Payment payment) {
this.payment = payment;
}
public void finishOrder(){
this.payment.pay();
}
}
Main
public class Main {
public static void main(String[] args) {
Order order = new Order();
Payment payment = new PaymentCardImpl();
order.setPayment(payment);
order.finishOrder();
}
}
There isn’t any Dependency injection in the code above. All the classes are highly coupled into each other. So it’s time to do some magic:
Payment
You can tell through the use of annotations which Payment type is the default implemented class
@ImplementedBy(PaymentCardImpl.class)
public interface Payment {
public void pay();
}
Order
Inject the payment type in the order. What we here say is: Inject the default paymentimplementation, which is set by the @implementedby annotation, into the payment field.
public class Order {
@Inject
private Payment payment;
....
Main
Lastly, we have to rewrite the main class. In this example, we get an order which uses the PaymentCard as payment type
public class Main {
public static void main(String[] args) {
Injector injector = Guice.createInjector();
Order order = injector.getInstance(Order.class);
order.finishOrder();
}
}
You can run this code. The output should be -I’ll pay with a credit card-. This code is so cool because if we want to switch to payment with cash at a later point, we just have to change the @implementedBy annotation in the interface and we are done.
Injection types
Guice knows different types of injection. The 3 most used are constructor, field and method injection (Examples are based on the above example)
Constructor injection
import com.google.inject.Inject;
public class Order {
private Payment payment;
@Inject
public Order(Payment payment){
this.payment=payment;
}
...
}
Method injection
import com.google.inject.Inject;
public class Order {
private Payment payment;
@Inject
public void setPayment(Payment payment) {
this.payment = payment;
}
...
}
Field injection
public class Order {
@Inject
private Payment payment;
...
}
Module based DI
It’s also possible to use Modules for injecting data. It’s maybe a little more work but I like it because all the configuration is in the same file. You can compare this with the xml-file in Spring applications.
Payment
Remove the @ImplementedBy annotation in the paymentinterface
public interface Payment {
public void pay();
}
MyModule
Next, create the Module class. It has to implement Module, which has one method: configure.
The code speaks for its self. You say nothing more than bind Payment to PaymentCard.
public class MyModule implements Module {
public void configure(Binder arg0) {
arg0.bind(Payment.class).to(PaymentCardImpl.class);
}
}
Instead of implementing Module, you can also extend AbstractModule. It’s just a matter of choice.
public class MyModule extends AbstractModule {
public void configure() {
bind(Payment.class).to(PaymentCardImpl.class);
}
}
Main
Lastly, we have to put the module into our injector and we are done.
public class Main {
public static void main(String[] args) {
MyModule module = new MyModule();
Injector injector = Guice.createInjector(module);
Order order = injector.getInstance(Order.class);
order.finishOrder();
}
}
The 4 lines in the main class can be written into 1 single line:
public class Main {
public static void main(String[] args) {
Guice.createInjector(new MyModule()).getInstance(Order.class).finishOrder();
}
}
Subclassing
In Guice, it’s easy to subclass an implementation.
Payment
public interface Payment {
public void pay();
}
PaymentCardImpl
public class PaymentCardImpl implements Payment{
public void pay() {
System.out.println("I'll pay with a credit card");
}
}
PaymentVisaCard
extend payment card with paymentvisacard
public class PaymentVisaCard extends PaymentCardImpl {
public void pay() {
System.out.println("I'll pay with a card from Visa");
}
}
Nothing special in the Main class
Main
public class Main {
public static void main(String[] args) {
Guice.createInjector(new MyModule()).getInstance(Payment.class).pay();
}
}
MyModule
You can specify the hierarchy in the moduleclass
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Payment.class).to(PaymentCardImpl.class);
bind(PaymentCardImpl.class).to(PaymentVisaCard.class);
}
}
If you run this code, the Visa Card will be used.
Annotationbindings
It’s also possible to use annotations for injection
Payment
There’s nothing special in the interface and implementations:
public interface Payment {
public void pay();
}
PaymentCardImpl
public class PaymentCardImpl implements PaymentCashImpl{
public void pay() {
System.out.println("I pay with a card");
}
}
PaymentCashImpl
public class PaymentCashImpl implements Payment {
public void pay() {
System.out.println("I pay cash");
}
}
Next, we have to created the annotations @cash and @Card
Cash
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 Cash { }
Card
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 Card { }
Main
public class Main {
public static void main(String[] args) {
Guice.createInjector(new PayModule()).getInstance(Order.class).finishOrder();
}
}
Module
You have to specify which annotations refers to which implementation in the moduleclass
public class PayModule extends AbstractModule{
@Override
protected void configure() {
bind(Payment.class).annotatedWith(Cash.class).to(PaymentCashImpl.class);
bind(Payment.class).annotatedWith(Card.class).to(PaymentCardImpl.class);
}
}
Order
Now you can use the annotation in your code to inject the right class
public class Order {
@Inject
private @Card Payment payment;
...
If you want to chane Cardpayments to Cashpayments, just change the @Card annotation to @Cash
Namedannotationbindings
You can also use named annotations. This is a Guice annotation where the value is specified inside the moduleclass.
The paymentinterface, the Main and the Cash and Card implementations are the same as the previous example.
Module
Give the annotations a name (“Cash” and “Card”)
public class PayModule extends AbstractModule{
@Override
protected void configure() {
bind(Payment.class).annotatedWith(Names.named("Cash")).to(CashPayment.class);
bind(Payment.class).annotatedWith(Names.named("Card")).to(CardPayment.class);
}
}
Main
* @author wim
*/
public class Main {
public static void main(String[] args) {
Injector inj = Guice.createInjector(new PayModule());
Order order = inj.getInstance(Order.class);
order.getPaymentCard().pay();
Guice.createInjector(new PayModule()).getInstance(Order.class).getPaymentCash().pay();
}
}
Order
Now you can use them in your order
public class Order {
private Payment paymentCash;
private Payment paymentCard;
@Inject
public void setPaymentCash(@Named("Cash") Payment payment){
this.paymentCash=payment;
}
public Payment getPaymentCash(){
return paymentCash;
}
@Inject
public void setPaymentCard(@Named("Card") Payment payment){
this.paymentCard=payment;
}
public Payment getPaymentCard(){
return paymentCard;
}
}
These annotations are very simple but I don’t recommend them. Guice doesn’t check them on spelling or validity so they are kinda error prone.
Instancebinding
With instancebinding, you can easily give a value to an instance
Module
Give a value to a name inside the module
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(String.class)
.annotatedWith(Names.named("PaymentType"))
.toInstance("...I'm a Visa Card...");
}
}
Main
public class Main {
public static void main(String[] args) {
String text = Guice.createInjector(new MyModule()).getInstance(Order.class).getText();
System.out.println(text);
}
}
Order
Use the annotation. The String text will be injected with “…I’m a Visa Card…”.
public class Order {
@Named("PaymentType")
@Inject
private String text;
public String getText(){
return text;
}
}
Providers
The last example is with the use of providers.
A provider can be used when you have to craete an object. You have to annotate it with the @Provides annotation and the return type is the bound type.
Payment
public interface Payment {
void pay();
int getAmount();
}
CashPayment
public class CashPayment implements Payment{
private int amount;
public void pay() {
System.out.println("Inside cashpayment");
}
public void setAmount(int amount){
this.amount=amount;
}
public int getAmount(){
return amount;
}
}
Main
public class Main {
public static void main(String[] args) {
Payment payment = Guice.createInjector(new MyModule()).getInstance(Payment.class);
payment.pay();
System.out.println(payment.getAmount());
}
}
MyModule
Inside the module class, we can construct the class and set the amount of payment.
public class MyModule extends AbstractModule{
@Override
protected void configure() {
}
@Provides
Payment providePayment(){
CashPayment pay = new CashPayment();
pay.setAmount(100);
return pay;
}
}
Conclusion
You can do more with Guice than described above but that’s up to you to find out.
I love Guice!! It’s pretty simple and works very good. You don’t have to use XML to bind your classes and it’s very lightweight. If you just wan’t to use Dependency Injection, Guice is in my opinion the number 1.
Comments (9)
Thanks for the post!
looks great
Nice Article.
I have started writing a series on Google Guice here, planning to add more soon. :-
http://anshu-manymoods.blogspot.com/2009/10/google-guice-part-1.html
http://anshu-manymoods.blogspot.com/2009/10/google-guice-part-2-inject-config-value.html
small thing: you dont have to write public on interfaces
Thank you for this fine article. I have a question, is it possible to overwrite a created instance into the Guice-Bindung during runtime, so that it can be used in other classes?
In your MyModule-Example you have this line:
arg0.bind(Payment.class).to(PaymentCardImpl.class);
now during the usage of the application, i want to do this:
PaymentCardImpl payment = new PaymentCardImpl();
payment.setPay(“currently calculated value”);
injector.OverwriteMyPaymentInstanceIntoGuiceWithThisNewInstance(payment)
When i use injection somewhere else and call getPay, i want to receive my previously stored information.
Is this possible?
Hi Mike,
Thanks for your question!
I’m not 100% sure about your question but I think you can accomply this with the use of a Singleton. Luckily, Guice has some some functionality for scopes. Here’s an example.
Main
public class Main { public static void main(String[] args) { Injector inj = Guice.createInjector(new MyModule()); Payment pay = inj.getInstance(Payment.class); pay.setAmount("test"); System.out.println(pay.getAmount()); Payment pay2 = inj.getInstance(Payment.class); System.out.println(pay2.getAmount()); } }Payment
public interface Payment { void pay(); void setAmount(String amount); String getAmount(); }CashPayment
public class CashPayment implements Payment { private String amount; public void pay() { System.out.println("Cash payment"); } public void setAmount(String amount) { this.amount = amount; } public String getAmount() { return amount; } }MyModule
public class MyModule extends AbstractModule{ @Override protected void configure() { bind(Payment.class).to(CashPayment.class).in(Scopes.NO_SCOPE); } }If you run this code, the output should be
There are 2 ways to implement the Singleton:
Mymodule
public class MyModule extends AbstractModule{ @Override protected void configure() { bind(Payment.class).to(CashPayment.class).in(Scopes.SINGLETON); } }If you run the code again, the result should be
Secondly, you can implement the singleton inside the implenetationclass with an annotation
@Singleton public class CashPayment implements Payment { private String amount; public void pay() { System.out.println("Cash payment"); } public void setAmount(String amount) { this.amount = amount; } public String getAmount() { return amount; } }Hopely, this gives an answer to your question.
Thank you, implementing the Singleton was the answer!
This is a pretty good tutorial. Had me thinking about how to apply Guice to my own projects within minutes.
[...] This is just a quick overview of Guice and there is probably quite a bit more I haven’t discovered yet. I need to give credit to the guy over at dev.eek.be who has written up a great introductory Guice tutorial. [...]
Google guice is cool!