Guice 2.0 is almost there, but not quite

Guice 2.0 is a clean and well-thought-out framework for modularization and dependency injection. It is really good, but it could be really really good with a couple of tweaks.

James Strachan created GuiceyFruit in an effort to move Guice from "really good" to "really really good", or say from 95% there to 99% there. Some patches from GuiceFruit got accepted in Guice 2.0 but some are still pending so GuiceyFruit maintains a patched version of Guice 2.0 for it's own needs.

Jersey depends on GuiceyFruit and the patched Guice 2.0 for it's Guice support (although it is possible to use "native" Guice if you are not using any GuiceyFruit features).

Currently with Guice 2.0 i cannot integrate support for the binding of JAX-RS types and annotations. I would like to be able to do the the following with Guice/Jersey/JAX-RS:

@RequestScoped
@Encoded
public class MyResource {
  private final String q;

  @Inject
  public class MyResource(@DefaultValue("foo") @QueryParam("q") String q) {
    this.q = q;
  }

}

But, currently as far as i am aware, it is not possible for two reasons:

  1. a provider binding does not have any context to what it is providing to; and
  2. an annotation that is not meta-annotated with @BindingAnnotation cannot be utilized as a binding annotation.

A good example of the complexity that results due to lack of support for 1) is presented in the section on Custom Injections of the Guice User's Guide. This section presents an example of how to support injection of a org.apache,log4j.Logger instance with fields annotated with the custom annotation @InjectLogger. The logger instance is initiated with the declaring class of the field.

The developer is required to implement a TypeListener that iterates through the fields of the class to find an appropriate field to inject a Logger, and a MembersInjector that injects the instance of the Logger onto the appropriate field.

While these interfaces are useful they are over-complex for such a use-case and the developer cannot use @Inject with Logger for constructor, method and field injection.

The injection of Logger can be reduced to a provider method of a module if Guice could support the injection of InjectionPoint:

 @Provides
 public Logger providerLogger(InjectionPoint ip) {
   return Logger.getLogger(ip.getMember().getDeclaringClass());
 }

With respect to the JAX-RS example a provider of the String type annotated with @QueryParam requires access to the annotations declared on the constructor parameter and the annotations declared on the defining class (it does not appear possible to get access to the former with InjectionPoint so that would require some tweaks as well).

Moving on to reason 2). If Guice supported the following binding in a module:

declareAnnotationAsBindingAnnotation(QueryParam.class);
bind(String.class)
  .annotatedWith(QueryParam.class)
  .toProvider(QueryParamProvider.class);

then it would be possible for Jersey to supply a Jersey-specific Guice module that supported all the Jersey and JAX-RS annotations in addition to enabling developers to easily add their own providers for such annotations.

If Guice could do that it would be 99% there IMHO and would be a really really good dependency injection framework.

Comments:

We decided to forgo injecting InjectionPoint for the time being because doing so would result in fragile code: http://code.google.com/p/google-guice/issues/detail?id=27&q=injectionpoint&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary%20Extension

For your use case, I'd create a child injector per request and bind the request parameters as constants. As a bonus, Guice will convert the types automatically. I'd be careful about changing the semantics of @Inject. For example, Guice uses @Inject(optional=true) instead of @DefaultValue. It would be a little weird if @DefaultValue worked here but not on other injection points.

The Guiceyfruit branch you linked to is from before Guice 2.0. Guice supports Guiceyfruit as an extension as of 2.0 (via TypeListener and InjectionListener).

Finally, we understand that Guice makes it a little harder for you as an extension developer, but Guice's design encourages you to do more work up front instead of at run time. That helps improve performance and catch errors earlier, both of which are best for users.

Posted by Bob Lee on August 30, 2009 at 08:58 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today