2

有没有办法与提供者绑定,该提供者在 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;

    ...
}
4

2 回答 2

3

这是不可能的。

如果@MyAnnotation是一个绑定注解,它将使用它的equals方法进行比较。@MyAnnotation(5) Resource将被绑定到@MyAnnotation(5) Resource,与 相比,那将根本不匹配@MyAnnotation(6) Resource。查看此 SO 答案以获取更多信息。与该答案一样,如果您愿意,您可以遍历可能的注释值并单独绑定每个注释值。

如果@MyAnnotation不是绑定注释,您将根本无法从您的提供者访问它。如此 SO answer中所述,将注入站点信息添加到提供程序或依赖项本身是一个被拒绝的功能。

您最好的选择是创建一个@Assisted注入(或手动工厂)来接受参数:

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

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

于 2013-02-05T04:17:54.147 回答
0

这是 Scala 中的一个示例(我喜欢使用 Scala 进行原型设计,毕竟它是 Java 穿着不同的衣服),我自己在Dynamic Google Juice injection 中根据注释的值对此进行了思考后想出了这个示例

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,但看起来它非常有效。这么多是不可能的。

于 2016-04-16T23:03:22.893 回答