Comment #2 on issue 758 by [email protected]: Increase support for
optional injection.
http://code.google.com/p/google-guice/issues/detail?id=758
Just to warn you, this is my first stab at trying to get this to work (and
I apologize for the Scala).
This is how it works prior to my changes:
/**
* This helper class helps recreate optional injection for not-required
auto-wired annotations.
*/
class OptArg[T] {
@Inject(optional = true)
private val t: T = null.asInstanceOf[T]
def arg(): Option[T] = Option(t)
}
class SecurityModule extends AbstractModule {
def configure() {
/* ... a bunch of bindings ... */
bindListener(Matchers.any(), new TypeListener {
def hear[I](literal: TypeLiteral[I], encounter: TypeEncounter[I]) {
def hearClass(cls: Class[_]) {
for (method <-
cls.getDeclaredMethods.filter(_.isAnnotationPresent(classOf[Autowired]))) {
val memberAnno =
Option(method.getAnnotation(classOf[Qualifier])).map(q =>
Names.named(q.value()))
val anno = method.getAnnotation(classOf[Autowired])
val params = method.getParameterTypes
if (params.length == 1) {
val provider = if (anno.required()) memberAnno match {
case Some(a) => encounter.getProvider(Key.get(params(0), a))
case None => encounter.getProvider(params(0))
} else memberAnno match {
case Some(a) =>
encounter.getProvider(Key.get(Types.newParameterizedType(classOf[OptArg[_]],
params(0)), a))
case None =>
encounter.getProvider(Key.get(Types.newParameterizedType(classOf[OptArg[_]],
params(0))))
}
encounter.register(new MembersInjector[I]() {
def injectMembers(instance: I) {
val member = provider.get()
if (anno.required()) {
method.invoke(instance, member.asInstanceOf[Object])
} else {
val optMember = member.asInstanceOf[OptArg[Object]]
optMember.arg().foreach(o => method.invoke(instance, o))
}
}
})
}
}
if (cls.getSuperclass != null) hearClass(cls.getSuperclass)
}
hearClass(literal.getRawType)
}
})
}
}
This, previous, piece of code was working fine (even if ugly) until I tried
to add Spring's @Qualifier annotation which is basically equivalent to
Guice's @Named annotation. So the problems are thus:
1) This doesn't work at all if I 'binder.requireExplicitBindings()' in my
Module (because it needs to try and create an OptArg on the fly to do the
optional injection, but it cannot). (I prefer to require explicit bindings
as often as possible; this is the first time I've found that it doesn't do
what I really want it to do.)
2) You can't automagically generate bindings for OptArg if it is annotated
with an @Named annotation. Which, technically, makes a lot of sense because
I actually want the field 't' to have the specific Named annotation and not
the wrapper class.
Now with my change, the code changes to something like this:
class SecurityModule extends AbstractModule {
def configure() {
binder.requireExplicitBindings()
/* ... a bunch of bindings ... */
bindListener(Matchers.any(), new TypeListener {
def hear[I](literal: TypeLiteral[I], encounter: TypeEncounter[I]) {
def hearClass(cls: Class[_]) {
for (method <-
cls.getDeclaredMethods.filter(_.isAnnotationPresent(classOf[Autowired]))) {
val memberAnno =
Option(method.getAnnotation(classOf[Qualifier])).map(q =>
Names.named(q.value()))
val anno = method.getAnnotation(classOf[Autowired])
val params =
method.getParameterTypes.asInstanceOf[Array[Class[AnyRef]]]
def getProvider(k: Key[AnyRef]): Provider[AnyRef] = if
(anno.required()) encounter.getProvider(k) else
encounter.getOptionalProvider(k)
if (params.length == 1) {
val key = memberAnno.map(a => Key.get(params(0),
a)).getOrElse(Key.get(params(0)))
val provider = getProvider(key)
encounter.register(new MembersInjector[I]() {
def injectMembers(instance: I) {
Option(provider.get()).foreach(o =>
method.invoke(instance, o))
}
})
}
}
if (cls.getSuperclass != null) hearClass(cls.getSuperclass)
}
hearClass(literal.getRawType)
}
})
}
}
Now, notice there are some major benefits in the second implementation:
1) I can require explicit bindings, because optional providers do not throw
errors if the provider cannot be resolved.
2) I can optionally inject objects that have bound annotations.
3) The code is cleaner without the need of a helper class nor do I need to
cast to the helper class before pulling out the real value.
I hadn't thought about looking up the injections inside of the
MembersInjector -- I had, apparently incorrectly, assumed that the injector
was not yet 'ready'. Is that your recommendation/preference?
--
You received this message because this project is configured to send all
issue notifications to this address.
You may adjust your notification preferences at:
https://code.google.com/hosting/settings
--
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice-dev.
For more options, visit https://groups.google.com/groups/opt_out.