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.


Reply via email to