Thursday, April 12, 2007

A Guice utility for binding to legacy classes

You're going along, happily Guicing your world, when you run into a snag. There's a class Legacy that you'd like to be able to bind things to, but you can't edit the source to add the @Inject annotations. It looks like this:


public class Legacy implements SomeInterface {
public Legacy(LegacyDependency dep, String str) {
...
}
public void initialize(LegacyConfig cfg) {
...
}
...
}

And you wish it looked like this:


public class Legacy implements SomeInterface {
@Inject public Legacy(LegacyDependency dep,
@MyAnnotation String str) {
...
}
@Inject public void initialize(LegacyConfig cfg) {
...
}
...
}

You're used to Guice by now, so right away you think, "No problem, I'll use a Provider!"


public class LegacyProvider
implements Provider<Legacy> {

public Legacy get() {
Legacy result = new Legacy(dep, str);
result.initialize(cfg);
return result;
}

@Inject LegacyDependency dep;
@Inject @MyAnnotation String str;
@Inject LegacyConfig cfg;
}

...

protected void configure() {
bind(SomeInterface.class)
.toProvider(LegacyProvider.class)
.in(WEB_APPLICATION_SCOPE);
}

So you've written a Provider<Legacy>, and it works. But now you have this extra class to maintain. It's not complicated, in fact it's quite trivial. It seems like something you shouldn't have had to write. You'd rather write something like this:


bind(SomeInterface.class)
.toProvider(

fromConstructor(Legacy.class,
Key.get(LegacyDependency.class),
Key.get(String.class, MyAnnotation.class))

.injecting("initialize",
Key.get(LegacyConfig.class))

)

.in(WEB_APPLICATION_SCOPE);

If this looks like something you want, check out my implementation in the DWR-Guice integration sources. The javadocs are there, too, but they aren't complete. It doesn't have anything to do with DWR, but it seems like a nice convenience to provide.

There are also several flavors of factory-method-based providers, so you don't have to write a special provider just to call a factory method.

I wrote it because even though I've been able to jettison a lot of baggage as I migrate from Spring to Guice, there are still Spring things I want to keep using, like JavaMailSender. I don't want to have to write provider implementations for each one.

2 comments:

Dhanji said...

This is an nice utility but as soon as I see injecting("initialize" ...) I feel like the rug has been pulled out from under and we're back in string and xml-land. =(

Wouldnt it be awesome if java had something like:

Method m = Legacy.initialize.@method;

I am grateful for the util however. =)

Thanks Tim--btw I cannot find your book (JCiP) in storeshelves in Australia. What gives? Isnt DC Holmes an aussie? I am dead keen to grab a copy (and will probably get one when up at javaone) but the suspense is deathly...

Tembrel said...

Stephen Colebourne has proposed a syntax for method literals, but until then this is the best I can think of. At least when it blows up, it blows up at bind-time in a very obvious way (if you've passed a Binder in).

I'll tell David Holmes to get cracking down under. :-)