custom annotation based injection points

238 views
Skip to first unread message

James Strachan

unread,
Oct 7, 2008, 11:21:50 AM10/7/08
to google...@googlegroups.com
There are lots of standards and frameworks which define their own
custom injection points via annotations. Here's a few off the top of
my head; I'm sure there's many more out there...

JSR 250 & EJB3 defines @Resouce for injecting objects from JNDI
JPA defines @PersistenceContext for injecting JPA resources usually
prebound to a transaction context etc
JAX-RS defines a number of them such as @PathParam, @HeaderParam, @Context etc
WebBeans defines @In
Spring defines @Autowired and a few others
Stripes defines @SpringBean
Apache Camel defines @EndpointInject and @Produces

Now you could just say, heck we should scrap all those specs and
frameworks and just use Guice for everything!! :) But being able to
use Guice as the DI framework for any of the above would be very cool
IMHO.

Now with the recent Constructor Interceptor code...
http://code.google.com/p/google-guice/issues/detail?id=78

its possible for any framework to implement these injection points
themselves; however each framework will end up re-implementing the
same kinda code that Guice already does (walking through all the
fields/methods looking for injection points and then doing the
reflection stuff etc); plus this would be kinda outside of Guice's
knowledge of the bindings & injection points so one day
tooling/validation in Guice wouldn't be aware of these custom
injection points and the handling & reporting of errors would probably
be inconsistent across the various frameworks.

So I've been wondering about adding a *small* SPI hook in Guice to
allow framework developers to create custom annotation based injection
points. i.e. registering in the Binder that an annotation creates a
custom injection point on a member (field/method). Then other
frameworks can just reuse the Guice injection mechanism; plus then
Guice is aware of all of the injection points in its model.


I've taken a stab at creating a patch; it turned out to be relatively
easy - I've just modified InjectionPoint ever so slightly to be aware
of both the standard @Inject injection points and custom injection
points. I'll just walk through how it all works - comments most
welcome.

Firstly to create a custom injection point you create an
implementation of a new SPI interface called
AnnotationProviderFactory; this class is then annotated with the
annotation type that is used as the custom injection point (which
allows us to setup the injection points before we start injecting
anything which avoids recursive injection etc).

For example here's an implementation of @Resource from JSR 250...

@InjectionAnnotation(Resource.class)
public class ResourceProviderFactory<T> implements
AnnotationProviderFactory<T> {
private final Context context;

@Inject
public ResourceProviderFactory(Context context) {
this.context = context;
}

public Provider<T> createProvider(AnnotatedElement member) {
Resource resource = member.getAnnotation(Resource.class);
Objects.nonNull(resource, "@Resource is missing!");
String name = getJndiName(resource, member);
return new Provider<T>() {...}
}
}

Then if this class is bound into a Binder, Guice will detect that this
is an AnnotationProviderFactory and associate it with all fields and
methods annotated with @Resource and then use the
AnnotationProviderFactory's Provider to create instances from the
member.

The nice thing is, the AnnotationProviderFactory can be injected
itself using the regular Guice approach - in the above case a JNDI
context is injected.

There are cases where frameworks try and integrate with Guice doing
some of the injections themselves and guice doing the rest; I'm hoping
through this approach we can just let Guice do all of the injections
in a single, consistent way with Guice being aware of all of the
injection points for tooling/validation/reporting purposes. This can
also solve issues that arise on the join between the IoC framework and
the underlying framework - e.g. with JAX-RS doing constructor
injection with some injections being Guice and others being JAX-RS
based injections. The same is true in other JSR 250/Spring/EJB3/JPA
style situations too.

Here's the first cut of a patch...
http://code.google.com/p/google-guice/issues/detail?id=258

in terms of code changes its fairly simple; we just pass in another
parameter (the custom injection points) to a number of InjectionPoint
related methods - and then the InjectionPoint can now store an
optional AnnotationProviderFactory if it defines a custom injection
point (as opposed to being a regular @Inject injection point).


Folks who just use 'vanilla' guice probably won't see what all the
fuss is about this patch; but folks who want to work with JSR 250 /
EJB3 / JPA / JAX-WS / JAX-RS / Spring beans / Stripes / Camel will
find this patch really useful (I know I do :); making it super simple
to add to a Binder custom injection points to implement most
annotation based specifications and frameworks around today easily in
Guice with minimal coding.

Thoughts?
--
James
-------
http://macstrac.blogspot.com/

Open Source Integration
http://open.iona.com

Reply all
Reply to author
Forward
0 new messages