如何绑定在Guice中使用注释值的提供程序?

时间:2013-02-04 03:55:56

标签: dependencies guice code-injection

有没有办法与提供商绑定解释Google Guice中目标的注释值?

示例:

bind(Resource.class)
    .annotatedWith(MyAnnotation.class)
    .toProvider(new MyProvider<MyAnnotation, Resource>{
        public Resource get(MyAnnotation anno){
            return resolveResourceByAnnoValue(anno.value());
        }
    });

我想通过带注释的绑定初始化Android Activity类的字段。 它必须通过它的唯一ID来获取多个资源。

原创方式:

public class TestActivity extends Activity{
    private TextView textView;
    private Button testButton;

    public void onAfterCreate(...){
        // set UI declaration resource.
        setContentView(R.layout.activity_test);

        // initialize fields, it must be done after setting ui definition.
        textView = (TextView) findViewById(R.id.textView);

        .... initialize other fields, hook them...

    ...
}

我想以声明的方式绑定UI及其字段,而不是实际上喜欢上面的内容:

@ResourceID(R.layout.activity_test)
public class TestActivity extends InjectiveActivity{
    @ResourceID(R.id.textView) // Auto generated static resource id constant
    private TextView textView;

    @ResourceID(R.id.testButton)
    private Button testButton;

    ...
}

2 个答案:

答案 0 :(得分:2)

这是不可能的。

如果@MyAnnotation是绑定注释,则会使用其equals方法进行比较。 @MyAnnotation(5) Resource将绑定到@MyAnnotation(5) Resource,与@MyAnnotation(6) Resource相比,它将完全不匹配。查看this SO answer了解更多信息。在该答案中,如果您愿意,可以遍历可能的注释值并单独绑定每个注释值。

如果@MyAnnotation不是绑定注释,您将无法从提供商处访问它。如this SO answer中所述,将注入站点信息添加到提供程序或依赖项本身是a rejected feature

最好的办法是创建一个@Assisted injection(或手动工厂)来接受参数:

class MyConsumer {
  final Resource resource;
  @Inject MyConsumer(Resource.Factory resourceFactory) {
    int previouslyAnnotatedValue = 5;
    this.resource = resourceFactory.createWithValue(previouslyAnnotatedValue);
  }
}

您也可以考虑使用Custom Injections,这将允许您使用除@Inject 之外的任意注释,这可能会使用您想要的运行时注释值。< / p>

答案 1 :(得分:0)

以下是Scala中的一个示例(我喜欢使用Scala进行原型设计,毕竟这是一个不同的服装中的Java),我在Dynamic Google Juice injection depending on value of an annotation

中自己想知道它之后提出了这个问题。
import java.lang.reflect.{Constructor, Parameter}
import java.util.concurrent.atomic.AtomicReference
import javax.inject.{Inject, Named, Provider}

import com.google.inject.matcher.Matchers
import com.google.inject.spi.ProvisionListener.ProvisionInvocation
import com.google.inject.{AbstractModule, Binder, Guice}
import com.google.inject.spi.{DependencyAndSource, ProviderInstanceBinding, ProvisionListener}
import com.typesafe.config.ConfigFactory
import net.codingwell.scalaguice.InjectorExtensions._
import net.codingwell.scalaguice.ScalaModule

import scala.collection.JavaConverters._

object GuiceExperiments extends App {

  val injector = Guice.createInjector(new MyModule())

  val some = injector.instance[Some]

  println(some)

  some.go()
}

trait Some {
  def go(): Unit
}

class Impl @Inject()(
                    @Named("a.a.a") hello: String,
                    @Named("a.a.b") bello: String,
                    @Named("a.b.a") kello: String

                    ) extends Some {
  override def go() = {
    println(hello)
    println(bello)
    println(kello)
  }
}

abstract class DynamicProvider[T >: Null](binder: Binder) extends Provider[T] {

  private[this] val nextValue = new AtomicReference[T]

  binder.bindListener(Matchers.any(), new ProvisionListener {

    private[this] def tryProvide(target: DependencyAndSource): Unit = {
      val dependency = target.getDependency
      val injectionPoint = dependency.getInjectionPoint
      val parameterIndex = dependency.getParameterIndex

      injectionPoint.getMember match {
        case constructor: Constructor[_] =>
          val parameter = constructor.getParameters()(parameterIndex)
          nextValue.set(getFor(parameter))
      }
    }

    override def onProvision[V](provision: ProvisionInvocation[V]): Unit = {

      provision.getBinding match {
        case binding: ProviderInstanceBinding[_] if binding.getUserSuppliedProvider eq DynamicProvider.this =>
          provision.getDependencyChain.asScala.lastOption.foreach(tryProvide)
        case _ => ()
      }
    }
  })

  final override def get(): T = nextValue.getAndSet(null)

  def getFor(parameter: Parameter): T
}

class MyModule extends AbstractModule with ScalaModule {

  override def configure(): Unit = {

    bind[Some].to[Impl]

    bind[String].annotatedWith[Named].toProvider(new DynamicProvider[String](binder) {
      override def getFor(parameter: Parameter): String = {
        if (parameter.isAnnotationPresent(classOf[Named])) {
          parameter.getAnnotation(classOf[Named]).value()
        } else {
          null
        }
      }
    })

  }

}

这只会插入@Named的值,但看起来非常棒。非常不可能。