5

I have a Servlet that has a lot of existing code. I'm trying to add dependency injection into one part of it. Currently I am doing it manually:

public class AdjustBookPriceHandler extends BookRequestHandler {
    @Override
    public void handleRequest(RequestState requestState, RequestData requestData, Object obj) {
        Book book = (Book) obj;
        long newPrice = Long.parseLong(requestData.getQueryParam("price");
        OfferRepository offerRepository = ((BookData) requestState.getData()).getOfferRepository();

        BookPriceAdjuster priceAdjuster = getBookPriceAdjuster();
        priceAdjuster.adjustPrice(newPrice);
    }

    protected BookPriceAdjuster getBookPriceAdjuster(RequestState requestState, RequestData requestData, Book book) {
        return new BookPriceAdjuster(book, offerRepository);
    }
}

Here the book and offer repository dependencies are injected into the BookPriceAdjuster through the constructor. The getBookPriceAdjuster method is there to allow classes that inherit from the AdjustBookPriceHandler to provide a different price adjuster.

I would like to start using a DI framework like Guice to reduce some of the boilerplate code that complex examples would introduce. However, I'm unsure of the best way to use it in this context.

How can I write bindings that would pull out the relevant dependencies from the "god" objects RequestState and RequestData? Or at this point would using a framework be just as complicated and messy?

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
ICR
  • 13,896
  • 4
  • 50
  • 78
  • What are the scope of these objects? They seem to be request-scope, but since you get your OfferRepository from them, I'm not so sure. It seems your request objects should be @RequestScoped, and that you should pull the OfferRepository out of them and inject it normally. – Jeremy Aug 16 '11 at 14:10
  • The objects themselves are request scoped, but (through IMO poor design decisions) load some data that is logically singleton scoped. – ICR Aug 16 '11 at 15:21
  • You might be able to have the request-scoped objects have the singleton-scoped objects injected into them when the request scope begins. – Jeremy Aug 16 '11 at 15:26
  • I don't want to add DI to the objects like RequestState and RequestData because they're complex legacy object with extremely poor test coverage. I'm trying to introduce DI on the leaves of the application with the view to then moving it into the more central components. – ICR Aug 16 '11 at 15:46

2 Answers2

0

How can I write bindings that would pull out the relevant dependencies from the "god" objects RequestState and RequestData?

You could start using Guice for dependency injection fairly easily here, depending on how your application is bootstrapped. Let's say you have a BooksModule.

public class BooksModule extends AbstractModule {
    protected void configure() {
        // Do Nothing
    }

    @Provides @RequestScoped
    BookData provideBookData(RequestState requestState) {
      return (BookData) requestState.getData();
    }

    @Provides @RequestScoped
    OfferRepository provideOfferRepository(BookData bookData) {
      return bookData.getOfferRepository();
    }
}

Now you can use Guice to inject the dependencies into your BookPriceAdjuster class.

@RequestScoped
public class BookPriceAdjuster {
    private final Book book;
    private final OfferRepository offerRepository;

    @Injected
    public BookPriceAdjuster(Book book, OfferRepository offerRepository) {
        this.book = book;
        this.offerRepository = offerRepository;
    }

    // whatever methods it has
}

And now you can use a provider for your BookPriceAdjuster inside your servlet.

public class AdjustBookPriceHandler extends BookRequestHandler {

    private final Provider<BookPriceAdjuster> bookPriceAdjusterProvider;

    @Injected
    public AdjustBookPriceHandler(Provider<BookPriceAdjuster> bookPriceAdjusterProvider) {
        this.bookPriceAdjusterProvider = bookPriceAdjusterProvider;
    }

    @Override
    public void handleRequest(RequestState requestState, RequestData requestData, Object obj) {
        Book book = (Book) obj;
        long newPrice = Long.parseLong(requestData.getQueryParam("price");

        BookPriceAdjuster priceAdjuster = bookPriceAdjusterProvider.get();
        priceAdjuster.adjustPrice(newPrice);
    }
}

To bootstrap the application, you'll need install the BooksModule using the Guice injector. How you go about this depends on how your application is currently bootstrapped. For servlets, I strongly recommend looking into the Guice-Servlet extension which lets you define your servlets and dependencies in "type-safe, idiomatic Java" instead of the web.xml file. Beautifully simple.

Or at this point would using a framework be just as complicated and messy?

As you can see above, you can slowly start injecting bindings (and providers) directly within an existing web app without changing your interfaces or calling patterns with ease. All that's left is the boostrapping... Just sneak the DI in there and let it slowly take over :)

Cody A. Ray
  • 5,869
  • 1
  • 37
  • 31
0

I'm not sure if I understand correctly what you are trying to do. The book is request scoped and offerRepository is singleton scoped? If so you can do something like this..

public class AdjustBookPriceHandler extends BookRequestHandler {

    // inject concrete instance with setter or constructor
    private BookPriceAdjusterProvider bookPriceAdjusterProvider;

    @Override
    public void handleRequest(RequestState requestState, RequestData requestData, Object obj) {
        Book book = (Book) obj;
        long newPrice = Long.parseLong(requestData.getQueryParam("price");

        BookPriceAdjuster priceAdjuster = bookPriceAdjusterProvider.getBookPriceAdjuster(book);
        priceAdjuster.adjustPrice(newPrice);
    }

}

public interface BookPriceAdjusterProvider {

    BookPriceAdjuster getBookPriceAdjuster(Book book);

}

public class MyBookPriceAdjusterProvider {

    // inject this through setter or constructor
    private OfferRepository offerRepository;

    protected BookPriceAdjuster getBookPriceAdjuster(Book book) {
        return new BookPriceAdjuster(book, offerRepository);
    }
}

This way, you get rid of the singleton scoped OfferRepository and can use some DI framework to do the injection for you. You can as well use different implementation of BookPriceAdjusterProvider if needed in your subclasses or so. Is this kind of what you wanted or have I misunderstood your objective completely?

Martin Lazar
  • 1,330
  • 16
  • 24