Guice 1.1 will probably have injected method interceptors, but until then those of us who need this kind of thing will have to roll our own.
[Update 2009-1-3] Guice 2 has (or will have, depending on how you view its release status) a requestInjection facility.
The way I've been doing it is similar to the way Stuart McCulloch suggested, but more automatic: I extend AbstractModule by- adding a registerForInjection method that takes a varargs array of objects that should be injected and
- overriding bindInterceptor to call registerForInjection(interceptors) before calling the superclass implementation.
To ensure that injectRegisteredObjects is called, I bind the module class to the module instance. Since I want to be able to do this for every instance of the module, I bind with a unique annotation instance each time. The private method ensureSelfInjection does this binding once only for each module instance, so I can safely call it in registerForInjection.
Here's the code:
/*** An extension of AbstractModule that provides
* support for member injection of instances
* constructed at bind-time; in particular,
* itself and MethodInterceptors.
*/
public abstract class ExtendedModule extends AbstractModule {
/**
* Overridden version of bindInterceptor that,
* in addition to the standard behavior,
* arranges for field and method injection of
* each MethodInterceptor in {@code interceptors}.
*/
@Override public void bindInterceptor(Matcher<? super Class<?>> classMatcher,
Matcher<? super Method> methodMatcher,
MethodInterceptor... interceptors) {
registerForInjection(interceptors);
super.bindInterceptor(classMatcher, methodMatcher, interceptors);
}
/**
* Arranges for this module and each of the given
* objects (if any) to be field and method injected
* when the Injector is created. It is safe to call
* this method more than once, and it is safe
* to call it more than once on the same object(s).
*/
protected <T> void registerForInjection(T... objects) {
ensureSelfInjection();
if (objects != null) {
for (T object : objects) {
if (object != null) toBeInjected.add(object);
}
}
}
@Inject private void injectRegisteredObjects(Injector injector) {
for (Object injectee : toBeInjected) {
injector.injectMembers(injectee);
}
}
private void ensureSelfInjection() {
if (!selfInjectionInitialized) {
bind(ExtendedModule.class)
.annotatedWith(getUniqueAnnotation())
.toInstance(this);
selfInjectionInitialized = true;
}
}
private final Set<Object> toBeInjected = new HashSet<Object>();
private boolean selfInjectionInitialized = false;
/**
* Hack to ensure unique Keys for binding different
* instances of ExtendedModule. The prefix is chosen
* to reduce the chances of a conflict with some other
* use of @Named. A better solution would be to invent
* an Annotation for just this purpose.
*/
private static Annotation getUniqueAnnotation() {
return named("ExtendedModule-" + count.incrementAndGet());
}
private static final AtomicInteger count = new AtomicInteger();
}
There's a follow-up posting on DWR-Guice applications.
6 comments:
Cool. I came up with a slightly less complete version. Good to see, that this problem was successfully tackled before.
The idea with that Named-Annotation is great, i missed something like that.
thanks for posting.
Dumb question -
return named("ExtendedModule-" + count.incrementAndGet());
what is named ? where is it defined?
Standard Guice 1.0:
import static com.google.inject.name.Names.named;
I've used a more encapsulated technique in the latest DWR code. See the guice.util package, and look at Numbered.java, NumberedImpl.java, and Numbers.java. Then, in AbstractModule.java, look at the ensureSelfInjection method, to see how it's used.
aha! thanks for the quick reply Tim, will check those out :)
Sweet. FYI, Guice 2 makes this a bit easier with its requestInjection() method.
Hi Tim,
a great post - it helped a lot. For a tutorial on Guice interceptors in general I chose a slightly different aproach by simply returning a list of injectees from the module.
You can find it here: http://musingsofaprogrammingaddict.blogspot.com/2009/01/guice-tutorial-part-2-method.html
I'd be happy on any comments,
Gunnar
Post a Comment