Wednesday, February 20, 2013

From Spring to Guice

For the past two and a half years I have worked primarily on standalone Java applications using Spring/Relational databases and messaging software such as Websphere MQ.

The Well Grounded Java Developer by Ben Evans and Martijn Verburg mentions Guice as an alternative to Spring for dependency injection. I like learning new technologies so I've decided to give it ago!

Here's my attempt at getting a little Guice application up and running. Firstly, I'm going to use Gradle and I'm going to get the Guice jars from the maven central repo. Here is my gradle build file:


apply plugin: 'java'
apply plugin: 'idea'

repositories {
  mavenCentral()
}

dependencies {
  compile 'com.google.inject:guice:3.0'
  testCompile 'junit:junit:4.11'
  testCompile 'org.mockito:mockito-all:1.9+'
}

I've also applied the idea plugin as I am trying out IntelliJ this week after seven years of Eclipse!

So I want a simple application that stores expenses. I am currently spending far too much money on coffee and want to start tracking it.

Lets start with a simple interface:

package com.batey.expense.store;

public interface ExpenseStorer {
    void storeExpense(Expense e);
    Expenses lookupExpenses(String name);
}

With a simple implementation:

package com.batey.expense.store;

import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

@Singleton
public class InMemoryExpenseStorer implements ExpenseStorer {
    private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryExpenseStorer.class);
    private static final Expenses NULL_EXPENSE = new Expenses();
    private Map<String, Expenses> expenses = new HashMap<>();
    @Override
    public void storeExpense(Expense e) {
        Expenses expenses1 = expenses.get(e.getName());
        if (expenses1 == null) {
            expenses1 = new Expenses();
            expenses.put(e.getName(), expenses1);
            expenses1.setGroupName(e.getName());
        }
        expenses1.getExpenses().add(e);
        LOGGER.info("Got {}", expenses1);
        LOGGER.info("Updated expenses {}", expenses);
    }

    @Override
    public Expenses lookupExpenses(String name) {
        Expenses expenses1 = expenses.get(name);
        LOGGER.info("Current expenses {}", expenses);
        if (expenses1 == null) {
            expenses1 = new Expenses();
            expenses1.setGroupName(name);
            expenses.put(name, expenses1);
        }
        LOGGER.info("Returning {}", expenses1);
        return expenses1;
    }
}

Now I want to use this class in another part of my system. Lets say we have another class called BudgetTracker:

package com.batey.expense.budget;

import com.batey.expense.store.ExpenseStorer;
import com.batey.expense.store.Expense;
import com.batey.expense.store.Expenses;
import com.google.inject.Inject;

import java.math.BigDecimal;

public class BudgetTracker {

    @Inject
    private ExpenseStorer expenseStorer;

    public void buySomething(String description, String who, BigDecimal howMuch) {
        Expense expense = new Expense();
        expense.setAmount(howMuch);
        expense.setName(who);
        expense.setDescription(description);
        expenseStorer.storeExpense(expense);
    }

    public BigDecimal howMuchHaveISpent(String who) {
        Expenses expenses = expenseStorer.lookupExpenses(who);
        return expenses.getTotal();
    }
}

Everything above was done without thinking about dependency injection. Then rather than instantiate an ExpenseStorer directly I then read through Google's getting started guide at http://code.google.com/p/google-guice/wiki/GettingStarted and added the Guide annotations in red.

So how do we run this? For that we need a Guide module:

package com.batey.expense.store;

import com.google.inject.AbstractModule;

public class ExpenseModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(ExpenseStorer.class).to(InMemoryExpenseStorer.class);
    }
}

And then I put together a very simple main method:

package com.batey.expense.budget;

import com.batey.expense.store.ExpenseModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

import java.math.BigDecimal;

public class Main {
    public static final void main(String[] args) {
        Injector injector = Guice.createInjector(new ExpenseModule());
        BudgetTracker budgetTracker = injector.getInstance(BudgetTracker.class);

        budgetTracker.buySomething("A tv", "Chris", new BigDecimal("100.00"));
        budgetTracker.buySomething("A cake", "Chris", new BigDecimal("50.00"));

        System.out.println("Chris has spent: " + budgetTracker.howMuchHaveISpent("Chris"));
    }
}

And then running this:

20:39:46.168 [main] INFO  c.b.e.store.InMemoryExpenseStorer - Got Expenses{expenses=[Expense{name='Chris', amount=100.00, description='A tv'}], total=0, groupName='Chris'}
20:39:46.171 [main] INFO  c.b.e.store.InMemoryExpenseStorer - Updated expenses {Chris=Expenses{expenses=[Expense{name='Chris', amount=100.00, description='A tv'}], total=0, groupName='Chris'}}
20:39:46.172 [main] INFO  c.b.e.store.InMemoryExpenseStorer - Got Expenses{expenses=[Expense{name='Chris', amount=100.00, description='A tv'}, Expense{name='Chris', amount=50.00, description='A cake'}], total=0, groupName='Chris'}
20:39:46.172 [main] INFO  c.b.e.store.InMemoryExpenseStorer - Updated expenses {Chris=Expenses{expenses=[Expense{name='Chris', amount=100.00, description='A tv'}, Expense{name='Chris', amount=50.00, description='A cake'}], total=0, groupName='Chris'}}
20:39:46.172 [main] INFO  c.b.e.store.InMemoryExpenseStorer - Current expenses {Chris=Expenses{expenses=[Expense{name='Chris', amount=100.00, description='A tv'}, Expense{name='Chris', amount=50.00, description='A cake'}], total=0, groupName='Chris'}}
20:39:46.172 [main] INFO  c.b.e.store.InMemoryExpenseStorer - Returning Expenses{expenses=[Expense{name='Chris', amount=100.00, description='A tv'}, Expense{name='Chris', amount=50.00, description='A cake'}], total=0, groupName='Chris'}
Chris has spent: 150.00



Key points:
  • We use the @Singleton on the ExpenseStorer to say we only want one of these in our application. Singleton is the default in spring: not so in Guice.
  • We use @Inject to identify a field needs injected. This could have also been done via the constructor. This is like the spring @Autowired
  • Inside the ExpenseModule we bind any injected ExpenseStorers to the InMemoryExpenseStorer implementation.
Success! From never having used Guice getting this together was very straightforward and took under thirty minutes. My initial thoughts of Guice for dependency injection:
  • It is very simple! I don't remember how long my first Spring application took me to get going but I bet it was longer than this!
  • No XML! Some people love configuration XML; others hate it. Personally, I'm sitting on the fence. In the latest applications I've written I've used a combination of annotations Java config along with XML config. Guice is pure Java config and I'd have to see a large scale project using it before passing judgement on that.
  • Standardisation! Guice is the first implementation of JSR330 - making DI standardized in Java can only be a good thing.
I'm going to keep building my expense application with Guice and will post more about it when I have used it more!

5 comments:

Unknown said...

Great Article

Online Java Training

Best Recommended books for Spring framework

Benjamin Jones said...

Selecting our gorgeous, verified, and experienced Independent Escorts Service and Call Girls in Aerocity would provide you spellbinding escort services that will offer you complete fulfillment and satisfaction and leave a long-lasting impression on you.Your Aerocity tour is always half-done without our Call Girls in Aerocity the real beauty in the city. We have the most gorgeous,Its so much insist of our loved customers our agency is happy to provide our Call Girls in Aerocity. We have hot and sexy females who are ready to hookup. Everyone are respectful with a quiet character. We have a wide range of Female escorts in Aerocity. Simply call us and recruit via given whatsapp option to the footer section.we are accessible day in and day out all over in Aerocity.To hire our Call Girls in Aerocity, you can speak with our call girl representative over the call. It is effortless to get Call Girls in Aerocity now. So, let us tell you about the benefit of our Call Girls in Aerocity because when you first desire something, you look for the pros of that thing. Call Girls in Aerocity, Delhi. One of the oldest Agency over hundreds of customer satisfatiction.

Benjamin Jones said...

Hello guys you! You will meet many Aerocity escorts but she will not give you full satisfaction. If all of you want to real & erotic satisfaction then you booked your Adorable Escorts Service in Aerocity. And take full enjoy with Gratifying Aerocity escorts services. Escorts Service in Aerocity perfectly delightful movements and they are ready for sensuality anytime you crave.You will be able to find Hot Escorts Service in Aerocity, Elite escorts, and others as well. There are galleries for all of these girls, making it easy for you to hand-select the girl you want to spend time with. Booking requires a simple phone call. Escorts Service in Aerocity are often available day and night. The sooner you book, the easier it will be to have a larger selection. The girls working with our Delhi escort agency as escorts are college girls, Escorts Service in Aerocity, and housewives. Our other young babes such as the College Escorts from Model Female Escorts Service in Aerocity and Mahipalpur agency are light-hearted and always seem to have a spring in their step.Individuals who are not wedded or have enormous sex advance ought to employ a Escorts Service in Aerocity escort young girl like me to get unwind.

Renzy Malik said...

Additionally, you can make a booking anytime of the night or day we make it easy to make it easy for you to book the most desirable models for your special occasion in rudrapur. With our 24/7 operating hours, it won't matter when you decide to meet with one of our models. You'll also have an enjoyable and stimulating time thanks to our fantastic models. They won't be missed in a flash.
what's app Udaipur girls number
phone call udaipur girls
hot call udaipur girls number.
call udaipur girls phone number
call udaipur girls phone number

Renzy Malik said...

I will set up the whole thing for you with the purpose that you will have the nice pleasure of desirous adoration in an undistributed organic system. My incredible delight is to make my folks fulfilled and upbeat. This has helped me get a huge collection of nice in elegance gentlemen and industrialists as my rehash clients.
what's app Dehradun girls number
what's app dehradun girls phone
phone call Nainital girls
hot call Haldwani girls number.
call Ramnagar girls phone number
call Rudrapur models phone number
call Rishikesh models phone number